测试发现了一个偶现bug,bugly上也统计到了,但是野指针引起的崩溃,一堆系统日志,也没法定位;
打开XCode僵尸对象机制后,在控制台拿到崩溃信息:
[AFJsonResponseSerializer release]: message sent to deallocted instance 0x282c60ae0
以下是复制粘贴 原文:Random crash at iOS 13.x AFNetworking解决 - 多线程争抢资源:
我使用的网络框架
我项目中使用的是XMNetworking,它内部也是基于AFN的封装。
问题分析
从观察来看,所有这个crash都是在子线程,且objc_retain
objc_release
都属于内存方面问题,objc_retain
极有可能是引用了一个已释放的对象,objc_release
像是对一个已释放的对象进行了多次释放,objc_msgSend
像是发送消息的对象和创建时匹配不上。所以要想方式找到复现的方式,然后再考虑怎么利用加锁进行解决,这一步也是经历了很久。
问题复现与解决
1.考虑复现问题,想了很多方法,最后慢慢尝试,加入测试代码,XMEngine 的 - (void)xm_dataTaskWithRequest:(XMRequest *)request completionHandler:(XMCompletionHandler)completionHandler
方法中加入处理
dataTask = [sessionManager dataTaskWithRequest:urlRequest
uploadProgress:nil
downloadProgress:nil
completionHandler:^(NSURLResponse *response, id responseObject, NSError *error) {
__strong __typeof(weakSelf)strongSelf = weakSelf;
#warning test code Simulate network multithreading return
dispatch_queue_t queue = dispatch_queue_create(@"test_queue", DISPATCH_QUEUE_CONCURRENT);
// 模拟多线程
for (NSInteger i = 0; i < 300; i ++) {
dispatch_async(queue, ^{
[strongSelf xm_processResponse:response
object:responseObject
error:error
request:request
completionHandler:completionHandler];
});
}
}];
同时考虑到可能对象创建问题,在懒加载时加入延时处理,以让多线程顺利争抢资源进入其中。
- (AFJSONRequestSerializer *)afJSONRequestSerializer {
if (!_afJSONRequestSerializer) {
#warning test code
[NSThread sleepForTimeInterval:0.2];
_afJSONRequestSerializer = [AFJSONRequestSerializer serializer];
}
return _afJSONRequestSerializer;
}
- (AFJSONResponseSerializer *)afJSONResponseSerializer {
if (!_afJSONResponseSerializer) {
#warning test code
[NSThread sleepForTimeInterval:0.2];
_afJSONResponseSerializer = [AFJSONResponseSerializer serializer];
// Append more other commonly-used types to the JSON responses accepted MIME types.
//_afJSONResponseSerializer.acceptableContentTypes = [NSSet setWithObjects:@"application/json", @"text/json", @"text/javascript", @"text/html", @"text/plain", nil];
}
return _afJSONResponseSerializer;
}
看到了对应的问题日志,基本算复现了此问题
*** -[AFJSONResponseSerializer release]: message sent to deallocated instance 0x6000035132d0
2.尝试加锁:
修改XMEngine.m 文件
- (void)xm_processResponse:(NSURLResponse *)response
object:(id)responseObject
error:(NSError *)error
request:(XMRequest *)request
completionHandler:(XMCompletionHandler)completionHandler {
NSError *serializationError = nil;
if (request.responseSerializerType != kXMResponseSerializerRAW) {
XMLock();
AFHTTPResponseSerializer *responseSerializer = [self xm_getResponseSerializer:request];
responseObject = [responseSerializer responseObjectForResponse:response data:responseObject error:&serializationError];
XMUnlock();
}
if (completionHandler) {
if (serializationError) {
completionHandler(nil, serializationError);
} else {
completionHandler(responseObject, error);
}
}
}
可以解决这个问题,但是还是没有定位到哪个具体问题,也就是加锁的代码太多了,也影响性能。
优化解决
XMEngine 里的所有懒加载加锁处理,下面这样处理之后,再用上面方式进行测试,发现无法复现了,且我线上版本也使用这种方式之后,再也没有看到类似的bug.
- (AFHTTPRequestSerializer *)afHTTPRequestSerializer {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_afHTTPRequestSerializer = [AFHTTPRequestSerializer serializer];
});
return _afHTTPRequestSerializer;
}
- (AFJSONRequestSerializer *)afJSONRequestSerializer {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_afJSONRequestSerializer = [AFJSONRequestSerializer serializer];
});
return _afJSONRequestSerializer;
}
总结
其实最后来看这个Bug其实和AFN可能没有关系,是我们在使用单例创建afn的时候需要注意线程安全,但是很奇怪ios 13之前的系统没有这个问题,所以不知道系统发生了什么变化。
发现问题到如何分析问题,如何模拟复现,这个过程思考了很久。
复现之后如何解决问题,也是尝试多次之后才找到了较优的方法。
网友评论