从 AFNetworking 的 url_session_manager_creation_queue 说起
0x0
- (NSURLSessionDataTask *)dataTaskWithRequest:(NSURLRequest *)request
completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler
{
__block NSURLSessionDataTask *dataTask = nil;
dispatch_sync(url_session_manager_creation_queue(), ^{
dataTask = [self.session dataTaskWithRequest:request];
});
[self addDelegateForDataTask:dataTask completionHandler:completionHandler];
return dataTask;
}
看 AFN 源码时看到这里,在使用 session 生成 task 时他使用了一个串行队列同步生成。一开始我以为是生成 task 开销大,但是一看是 sync 又觉得奇怪。
搜索网络看到了真相。
static dispatch_queue_t url_session_manager_creation_queue() {
static dispatch_queue_t af_url_session_manager_creation_queue;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
af_url_session_manager_creation_queue = dispatch_queue_create("com.alamofire.networking.session.manager.creation", DISPATCH_QUEUE_SERIAL);
});
return af_url_session_manager_creation_queue;
}
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();
}
}
原来是这个函数在 iOS 8 里没考虑并发情况下生成的 taskIdentifier 会冲突重复。于是用一个同步队列避免这个情况。
0x1
一年多前发生过相关的问题。
美团在 9.5 版本时期出现崩溃,崩溃的现象是在拿到 response 使用的时候对 string 发去了 dict 才有的方法,或者 array 的方法。
最早短连的时候用的是一个请求用一个 session 承载,后来太慢了。于是复用 session,但是苹果的性能貌似有点问题,超过 30 task 积累在 session 里,后面积压的 task 可能发不出去或者收不到 response。
于是设置了一个 30 的阈值,超过了就起第二个 session。
结果呢,又是 taskIdentifier 重复了,导致回调的时候张三配李四了。
因为 task.taskIdentifier 这货文档里写了
tasks in other sessions may have the same taskIdentifier value.
不同 session 之间 taskIdentifier 可能相同
0x2
所以这是一个高可用 id 生成器的问题?