我正在使用GCD
异步发送HTTP
请求。以下代码不起作用:
dispatch_async(connectionQueue, ^{
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init];
[request setURL:[NSURL URLWithString:[NSString stringWithFormat:someURL]]];
NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:request delegate:self];
[connection start];//Not working
});
以上代码根本不起作用。我没有收到任何NSURLConnectionDelegate方法的回电。
但是当我尝试以下代码时,一切正常,我得到了适当的回调
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init];
[request setURL:[NSURL URLWithString:[NSString stringWithFormat:someURL]]];
NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:request delegate:self];
dispatch_async(connectionQueue, ^{
[connection start]; // working fine. But WHY ????
});
有人可以解释阻止/ GCD这种奇怪的行为吗?
答案 0 :(得分:2)
在代码示例的第一部分中尝试此操作 -
dispatch_async(dispatch_get_main_queue(), ^(void){
NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:request delegate:self];
[connection start];
}
如果将连接放在后台队列中,则在队列完成后会将其推回,因此您无法获得委托回调。连接可以在主队列中,因此它将保留在主运行循环中以进行回调。或者,您可以创建自己的runloop,根据其他人的建议为您处理后台操作。
答案 1 :(得分:2)
当使用NSURLConnection进行异步通信时,您需要将其实例化的线程附加到其RunLoop的连接,以便对该连接的委托方法进行该线程轮询。
在不依赖主线程的RunLoop的情况下异步实例化NSURLConnection的正确方法如下:
// Done within a Grand Central Dispatch block, or NSOperation.
// We do not start the connection here because we still need to attach the connection to the RunLoop of the current thread and handle how it will communicate responses back to the caller.
theConnection = [[NSURLConnection alloc]initWithRequest:request delegate:self startImmediately:NO];
// A previously instantiated NSOperationQueue for managing the delegate callbacks if-and-when they occur.
// [theConnection setDelegateQueue:delegateQueue];
/*
// Other NSURLConnection logic, etc.
*/
// We start the connection manually after properly establishing how it will poll and respond to events.
[theConnection start];
// This is for the RunLoop of the current thread. (Only needed for < iOS 6 compatibility)
// If this method is executed inside a GCD block or NSOperation, it will be the RunLoop of the thread run in-parallel to the Main Thread.
[[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:30.0]];
// From here, you can alter the time interval to coincide with a specific "time-out" event you'd like to occur.
其中“theConnection”是当前类“NSURLConnection”的成员变量。此外,一旦连接收到响应,您将需要创建一个NSOperationQueue成员变量来管理委托回调。这些调用将异步传回给正在运行连接的线程。
从那里,您可以使用正确的NSURLConnection委托方法返回数据。
为您的线程使用Grand Central Dispatch或Operation Queues的好处是Threading和RunLoop机制已经内置;您不必在其中手动分配具有自己的RunLoop的其他线程。这消除了创建后台线程以管理异步服务器调用的两步冗余。
我希望这足以让您走上正确的道路,为您的应用程序创建真正的异步网络模型。 :)
答案 2 :(得分:1)
NSURLConnection将始终在其创建的线程上执行数据获取(alloc init)。这解释了为什么它会以第二种方式工作。第一种方法可行,但线程在您能够从NSURLConnection接收任何信息之前就已消失。 NSURLConnection已经允许异步下载,但如果你想甚至异步运行数据处理,你应该使用以下方法:
+ (void)sendAsynchronousRequest:(NSURLRequest *)request queue:(NSOperationQueue *)queue completionHandler:(void (^)(NSURLResponse*, NSData*, NSError*))handler
该方法存在一些限制,例如身份验证受限制,您无法跟踪到目前为止已下载了多少文档。您还必须指定您创建的NSOperationQueue,默认队列是主循环队列。
答案 3 :(得分:1)
您可以为该连接启动临时NSOperationQueue。只要连接需要,该队列就会存在。基本上,NSOperationQueue确保您通过旋转线程处理每个委托回调来排队并处理委托回调。 (在大多数情况下,它是相同的后台线程,在下载新数据时,或者当连接失败,连接完成加载等时进入休眠状态并恢复)。完成此队列设置后,委托回调将开始进入您的应用程序。
dispatch_async(connectionQueue, ^{
connection = [[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately:NO];
NSOperationQueue __autoreleasing *tempQueue = [[NSOperationQueue alloc] init];
[connection setDelegateQueue:tempQueue];
[connection start];
});
如果您选择RunLoop,那么管理runloop会给您带来额外的负担。