这是一个有趣的问题,答案是关于如何交互和协同工作的NSOperation
语义NSURLConnection
。
AnNSURLConnection
本身就是一个异步任务。这一切都发生在后台,并定期调用其委托与结果。当你启动NSURLConnection
它时,它使用调度它的 runloop 来调度委托回调,因此 runloop 必须始终在你正在执行的线程上运行NSURLConnection
。
因此-start
,我们的方法AFURLConnectionOperation
总是必须在操作完成之前返回,以便它可以接收回调。这要求这AFURLConnectionOperation
是一个异步操作。
来自:https ://developer.apple.com/library/mac/documentation/Cocoa/Reference/NSOperation_class/index.html
对于相对于当前线程异步运行的操作,property 的值为 YES,对于在当前线程上同步运行的操作,property 的值为 NO。此属性的默认值为 NO。
但AFURLConnectionOperation
会覆盖此方法并按YES
我们的预期返回。然后我们从类描述中看到:
当您调用异步操作的 start 方法时,该方法可能会在相应任务完成之前返回。异步操作对象负责在单独的线程上调度其任务。该操作可以通过直接启动新线程、调用异步方法或将块提交到调度队列执行来实现。当控制权返回给调用者时操作是否正在进行实际上并不重要,只是它可能正在进行。
AFNetworking 使用一个类方法创建一个单一的网络线程,它调度所有NSURLConnection
对象(及其产生的委托回调)。这是来自的代码AFURLConnectionOperation
+ (void)networkRequestThreadEntryPoint:(id)__unused object {
@autoreleasepool {
[[NSThread currentThread] setName:@"AFNetworking"];
NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
[runLoop addPort:[NSMachPort port] forMode:NSDefaultRunLoopMode];
[runLoop run];
}
}
+ (NSThread *)networkRequestThread {
static NSThread *_networkRequestThread = nil;
static dispatch_once_t oncePredicate;
dispatch_once(&oncePredicate, ^{
_networkRequestThread = [[NSThread alloc] initWithTarget:self selector:@selector(networkRequestThreadEntryPoint:) object:nil];
[_networkRequestThread start];
});
return _networkRequestThread;
}
这是AFURLConnectionOperation
显示他们NSURLConnection
在所有运行循环模式下调度 AFNetwokring 线程的运行循环的代码
- (void)start {
[self.lock lock];
if ([self isCancelled]) {
[self performSelector:@selector(cancelConnection) onThread:[[self class] networkRequestThread] withObject:nil waitUntilDone:NO modes:[self.runLoopModes allObjects]];
} else if ([self isReady]) {
self.state = AFOperationExecutingState;
[self performSelector:@selector(operationDidStart) onThread:[[self class] networkRequestThread] withObject:nil waitUntilDone:NO modes:[self.runLoopModes allObjects]];
}
[self.lock unlock];
}
- (void)operationDidStart {
[self.lock lock];
if (![self isCancelled]) {
self.connection = [[NSURLConnection alloc] initWithRequest:self.request delegate:self startImmediately:NO];
NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
for (NSString *runLoopMode in self.runLoopModes) {
[self.connection scheduleInRunLoop:runLoop forMode:runLoopMode];
[self.outputStream scheduleInRunLoop:runLoop forMode:runLoopMode];
}
//...
}
[self.lock unlock];
}
这里[NSRunloop currentRunloop]
检索 AFNetworking 线程上的运行循环,而不是从mainRunloop
该-operationDidStart
线程调用方法。作为奖励,我们也可以outputStream
在后台线程的 runloop 上运行。
现在AFURLConnectionOperation
等待回调并随着网络请求的进行NSURLConnection
更新自己的NSOperation
状态变量(cancelled
, finished
, )。executing
AFNetworking 线程反复旋转它的运行循环,以便NSURLConnections
从潜在的许多AFURLConnectionOperations
调度它们的回调中调用它们并且AFURLConnectionOperation
对象可以对它们做出反应。
如果您总是计划使用队列来执行您的操作,将它们定义为同步会更简单。但是,如果您手动执行操作,您可能希望将操作对象定义为异步的。定义异步操作需要做更多的工作,因为您必须监控任务的持续状态并使用 KVO 通知报告该状态的变化。但是,在您希望确保手动执行的操作不会阻塞调用线程的情况下,定义异步操作很有用。
另请注意,您也可以通过调用并观察它直到返回来使用NSOperation
不带 a的 a 。如果被实现为同步操作并阻塞了等待完成的当前线程,它将永远不会真正完成,因为它将在当前运行循环上安排它的回调,因为我们会阻塞它而不会运行。因此,为了支持这种有效的使用场景,我们必须使异步。NSOperationQueue
-start
-isFinished
YES
AFURLConnectionOperation
NSURLConnection
NSURLConnection
NSOperation
AFURLConnectionOperation
问题的答案
是的,AFNetworking 创建了一个线程,用于调度所有连接。线程创建是昂贵的。(这就是创建 GCD 的部分原因。GCD 为您保留一个运行的线程池,并根据需要在不同的线程上分派块,而无需自己创建、销毁和管理线程)
该处理不在后台 AFNetworking 线程上完成。AFNetworking 使用 的completionBlock
属性进行NSOperation
处理,当finished
设置为时执行YES
。
无法保证完成块的确切执行上下文,但通常是辅助线程。因此,您不应使用此块来执行任何需要非常特定的执行上下文的工作。相反,您应该将该工作分流到应用程序的主线程或能够执行此操作的特定线程。例如,如果您有一个自定义线程来协调操作的完成,您可以使用完成块来 ping 该线程。
HTTP 连接的后处理在AFHTTPRequestOperation
. 此类创建一个调度队列,专门用于在后台转换响应对象,并将工作分流到该队列上。看这里
- (void)setCompletionBlockWithSuccess:(void (^)(AFHTTPRequestOperation *operation, id responseObject))success
failure:(void (^)(AFHTTPRequestOperation *operation, NSError *error))failure
{
self.completionBlock = ^{
//...
dispatch_async(http_request_operation_processing_queue(), ^{
//...
我想这引出了一个问题,他们是否可以写信AFURLConnectionOperation
不做一个线程。我认为答案是肯定的,因为有这个 API
- (void)setDelegateQueue:(NSOperationQueue*) queue NS_AVAILABLE(10_7, 5_0);
这意味着将您的委托回调安排在特定的操作队列上,而不是使用运行循环。但是当我们看到 AFNetworking 的遗留部分时,该 API 仅在 iOS 5 和 OS X 10.7 中可用。查看 Github 上的责备视图,AFURLRequestOperation
我们可以看到,mattt 实际上+networkRequestThread
在 2011 年 iPhone 4s 和 iOS 5 发布的那一天巧合地编写了该方法!因此,我们可以推断线程存在,因为在编写线程时,我们可以看到创建线程并在其上调度连接是NSURLConnection
在异步子类中运行时从后台接收回调的唯一方法NSOperation
。