Objective c 目标C:不使用委托方法的异步/后台发布?

Objective c 目标C:不使用委托方法的异步/后台发布?,objective-c,ios,cocoa-touch,nsurlconnection,Objective C,Ios,Cocoa Touch,Nsurlconnection,我需要对我的服务器进行一些POST调用,但我不需要阻塞主线程。据我所知,NSMutableURLRequest和NSURLConnection不是线程安全的,因此最好使用NSURLConnection的异步方法 关于这一点,我的问题是,如何将它很好地打包到一个方法中,而不必使用委托方法?我更愿意做: NSData *returnedData = [Utility postDataToURL:@"some string of data"]; 这就是使用以下方法轻松完成的方法: [NSURLCon

我需要对我的服务器进行一些POST调用,但我不需要阻塞主线程。据我所知,
NSMutableURLRequest
NSURLConnection
不是线程安全的,因此最好使用
NSURLConnection
的异步方法

关于这一点,我的问题是,如何将它很好地打包到一个方法中,而不必使用委托方法?我更愿意做:

NSData *returnedData = [Utility postDataToURL:@"some string of data"];
这就是使用以下方法轻松完成的方法:

[NSURLConnection sendSynchronousRequest:serviceRequest returningResponse:&serviceResponse error:&serviceError];
将所有内容都保存在一个方法中,然后从中返回数据,这真是太好了

是否有任何基于块的方法用于此?当我需要为大约50个不同的调用编写方法,并且每个调用都需要使用相同的委托方法时,这就成了一个问题。我走错方向了吗


这只需要用于iOS5。

iOS5.0>
您可以使用sendAsynchronousRequest方法查看,它使用块。如果您也想支持iOS 4.0>,那么您必须编写一个自己的基于块的异步URL加载,这相当容易编写。你最好还是使用它

但是我不需要阻塞主线程。据我所知,NSMutableURLRequest和NSURLConnection不是线程安全的,因此最好使用NSURLConnection的异步方法


您不想进行同步网络连接,因为它会阻塞从哪个线程调用它的线程(如果是主线程,则情况更糟)。您可以在主线程上进行异步网络连接。如果要在非主线程上调用NSURLConnection,则必须在该线程上创建一个RunLoop(如果不这样做,则不会调用NSURLConnection的委托方法)。

我使用facade方法对发出同步调用的内部工作线程进行操作排队。根据您发送电话的速率,它可能会起作用。例如:

// Presented in @interface
-(void)sendPostRequest {
   // Last chance to update main thread/UI
   NSInvocationOperation *op = [[[NSInvocationOperation alloc] initWithTarget:self selector:@selector(sendPostRequest_internal) object:nil] autorelease];
   [opQueue addOperation:op];
}

// Hidden in @implementation
-(void)sendPostRequest_internal {
   NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];

   NSURLRequest *request = // yadda, you might use NSURLMutableRequest
   NSURLResponse *response = nil;
   NSError *error = nil;
   NSData *data = [NSURLConnection sendSynchronousRequest:request returningResponse:response error:error];

   // process data, retain things as needed, post results using performSelectorOnMainThread:

   [pool release];
}

就我的目的而言,它工作得很好,但您可能需要深入研究异步的内容,这还不算太糟糕。

iOS 5添加了
sendAsynchronousRequest:queue:completionHandler:
,我认为这正是您想要的。我已经将代码设置为在可用的情况下使用它,但如果不可用,则返回到后台GCD队列上执行同步获取并跳转到主线程上。后者的能效较低,但它只是为了维护遗留支持

if([NSURLConnection respondsToSelector:@selector(sendAsynchronousRequest:queue:completionHandler:)])
{
    // we can use the iOS 5 path, so issue the asynchronous request
    // and then just do whatever we want to do
    [NSURLConnection sendAsynchronousRequest:request
        queue:[NSOperationQueue mainQueue]
        completionHandler:
        ^(NSURLResponse *response, NSData *data, NSError *error)
        {
            [self didLoadData:data];
        }];
}
else
{
    // fine, we'll have to do a power inefficient iOS 4 implementation;
    // hop onto the global dispatch queue...
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0),
    ^{
        // ... perform a blocking, synchronous URL retrieval ...
        NSError *error = nil;
        NSURLResponse *urlResponse = nil;
        NSData *responseData =
            [NSURLConnection sendSynchronousRequest:request returningResponse:&urlResponse error:&error];

        // ... and hop back onto the main queue to handle the result
        dispatch_async(dispatch_get_main_queue(),
        ^{
            [self didLoadData:responseData];
        });
    });
}

在生产代码中,您实际上要检查
错误
s和HTTP响应代码(从您的角度来看,服务器404响应可能与连接故障一样是一个错误),显然。

我在5.0之前就遇到了这个问题,所以我制作了一个小类来处理NSURLConnection委托协议,并为调用者提供了一个带有闭包的接口:

更好的连接

@property (retain, nonatomic) NSURLRequest *request;
BetterNSURLConnection.m

@property (retain, nonatomic) NSURLConnection *connection;
@property (retain, nonatomic) NSHTTPURLResponse *response;
@property (retain, nonatomic) NSMutableData *responseData;

@property (copy, nonatomic) void (^completionBlock)(id, NSHTTPURLResponse *);
@property (copy, nonatomic) void (^errorBlock)(NSError *);
。。。您可以添加typedef使这些块签名更漂亮…然后:

@synthesize connection = _connection;
@synthesize response = _response;
@synthesize responseData = _responseData;
@synthesize completionBlock = _completionBlock;
@synthesize errorBlock = _errorBlock;
@synthesize request=_request;


- (void)startWithCompletion:(void (^)(id, NSHTTPURLResponse *))completionBlock error:(void (^)(NSError *))errorBlock {

    [UIApplication sharedApplication].networkActivityIndicatorVisible = YES;

    self.completionBlock = completionBlock;
    self.errorBlock = errorBlock;
    self.responseData = [NSMutableData data];

    NSURLConnection *connection = [NSURLConnection connectionWithRequest:self.request delegate:self];
    self.connection = connection;
    [self.connection start];
    [connection release];
}
。。。然后对学员执行以下操作:

- (void)connection:(NSURLConnection *)aConnection didReceiveResponse:(NSHTTPURLResponse *)response {

    [self.responseData setLength:0];
    self.response = response;
}

- (void)connection:(NSURLConnection *)aConnection didReceiveData:(NSData *)data {

    [self.responseData appendData:data];
}

- (void)connection:(NSURLConnection *)aConnection didFailWithError:(NSError *)error {

    [UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
    self.errorBlock(error);
    self.connection = nil;
}

- (void)connectionDidFinishLoading:(NSURLConnection *)aConnection {

    [UIApplication sharedApplication].networkActivityIndicatorVisible = NO;

    if (self.response.statusCode >= 400) {
        self.errorBlock(error);
    } else {
        // i do json requests, and call a json parser here, but you might want to do something different
        id result = [self parseResponse:self.responseData];
        self.completionBlock(result, self.response);
    }
    self.connection = nil;
}

你说你不想阻止主线程,但你说你想做的很明显是同步的,也就是说,它会阻止主线程(或者至少是你调用它的线程)。这就是一个例子,说明我希望能够很容易地返回数据。尽管其余的与我的答案基本相同,我不确定我是否同意最后一句话;这是一个可以在大约50行代码中实现5或4次的功能,因此合并一个大型第三方库听起来并不会让海报变得更好,特别是考虑到支持和测试的影响。@Tommy对于这个特定任务,我同意您不需要第三方库。如果您想在实用程序类中添加更多功能,如处理REST参数的简易方法、图像缓存等,我建议您使用它的原因是向前的。尽管您可以根据自己的需求不断添加功能,你的设计可能会变得复杂,而这些第三方框架也很方便。我正在制作一个iOS 5应用程序,所以它工作得非常完美!非常感谢。是否最好将队列设置为[NSOperationQueue mainQueue],而不是我自己使用的名称?这真的取决于您。我使用了
mainQueue
,因为我在完成处理程序中做了一些UIKit工作-您要标识的队列是您希望完成处理程序发生的位置,因此如果您希望在后台发生这种情况,请传入您的备用队列。哦,真的。好的,如果在我的完成处理程序中,我想更新UI,那么我需要使用
mainQueue
?UIKit工作通常应该在主线程上执行,但是如果你想做一些像昂贵的解析操作,然后进行接口更新,在主队列之外的其他对象上执行完成处理程序,然后跳转到主队列或线程上发布UI更新,这是有意义的。