AFNetworking 源码阅读

AFNetworking 3.0 发布了,这个版本去除了 NSURLConnectionOperation,使用 NSURLSession 代替,于是,这个版本只支持 iOS 7以及以上版本,代码也相应的精简了不少,于是利用一周时间阅读了一下 AFNetworking 3.0 的源码。

技巧

#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wgnu"
//code
#pragma clang diagnostic pop

使用 :? 符号,忽略编译器的警告

iOS bug fix

AFURLSessionManager.m

// 解决NSURLSessionTask 的 taskIdentifier 在并发的情况下不是唯一的 bug,苹果在 iOS8 的时候解决了这个 bug
static void url_session_manager_create_task_safely(dispatch_block_t block) {
if (NSFoundationVersionNumber < NSFoundationVersionNumber_With_Fixed_5871104061079552_bug) {
// Fix of bug
// Open Radar:http://openradar.appspot.com/radar?id=5871104061079552 (status: Fixed in iOS8)
// Issue about:https://github.com/AFNetworking/AFNetworking/issues/2093
dispatch_sync(url_session_manager_creation_queue(), block);
} else {
block();
}
}

AFURLSessionManager.h

/**
Whether to attempt to retry creation of upload tasks for background sessions when initial call returns `nil`. `NO` by default.

@bug As of iOS 7.0, there is a bug where upload tasks created for background tasks are sometimes `nil`. As a workaround, if this property is `YES`, AFNetworking will follow Apple's recommendation to try creating the task again.

@see https://github.com/AFNetworking/AFNetworking/issues/1675
*/

// iOS7.0 的一个 bug,当创建 后台的 upload task 的时候可能是 nil,这个值为 YES 的时候会尝试重新创建
@property (nonatomic, assign) BOOL attemptsToRecreateUploadTasksForBackgroundSessions;

多线程

dispatch_semaphore的使用

AFURLSessionManager.m 里面的 tasksForKeyPath: 方法,通过引入信号量的方式把NSURLSession的异步方法 getTasksWithCompletionHandler: 变成了同步方法

- (NSArray *)tasksForKeyPath:(NSString *)keyPath {
__block NSArray *tasks = nil;
// 这里是把本来异步的getTasksWithCompletionHandler方法变成了同步的方式了,通过引入信号量的方式,等待异步方法获取到tasks,然后再返回
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
[self.session getTasksWithCompletionHandler:^(NSArray *dataTasks, NSArray *uploadTasks, NSArray *downloadTasks) {
if ([keyPath isEqualToString:NSStringFromSelector(@selector(dataTasks))]) {
tasks = dataTasks;
} else if ([keyPath isEqualToString:NSStringFromSelector(@selector(uploadTasks))]) {
tasks = uploadTasks;
} else if ([keyPath isEqualToString:NSStringFromSelector(@selector(downloadTasks))]) {
tasks = downloadTasks;
} else if ([keyPath isEqualToString:NSStringFromSelector(@selector(tasks))]) {
tasks = [@[dataTasks, uploadTasks, downloadTasks] valueForKeyPath:@"@unionOfArrays.self"];
}

dispatch_semaphore_signal(semaphore);
}];

dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);

return tasks;
}

上面代码中 @unionOfArrays.self 参考:http://nshipster.com/kvc-collection-operators/

作者声明二级指针

// 这是一个指向指针的指针,二级指针
// https://www.zhihu.com/question/35661618
static void *AFHTTPRequestSerializerObserverContext = &AFHTTPRequestSerializerObserverContext;

这里是为了做 KVO 的时候给 context 赋值,便于区分不同的 context,由于这个AFHTTPRequestSerializerObserverContext的内存地址是在编译的时候就决定了,所以这么声明确实可以保证 context 不同,是个比较讨巧的方式

static 方法

作者在多处使用 static 方法

参考 & 代码地址

边阅读边标注,阅读标注后的代码地址:AFNetworking 3.0

参考: