zoukankan      html  css  js  c++  java
  • Random crash at iOS 13.x AFNetworking解决

    问题描述

    线上版本从ios 13.x 之后出现很多afn相关的crash,在 AFNetworking 的 github 上对应的issue#4591,我在这issue下也添加我的评论的解决demo
    下面是crash堆栈,有些crash并不只是下面这种最终crash在objc_retain,有些则是objc_releaseobjc_msgSend之类的

    我使用的网络框架

    我项目中使用的是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之前的系统没有这个问题,所以不知道系统发生了什么变化。
    发现问题到如何分析问题,如何模拟复现,这个过程思考了很久。
    复现之后如何解决问题,也是尝试多次之后才找到了较优的方法。

    相关Demo复现

    https://github.com/LiJinShi/NetWorkingBug

  • 相关阅读:
    日期转换DateTime
    linux 常用命令
    springcloud集成 xxl-job
    maven-阿里云镜像
    mysql 8.0+忘记root密码-linux
    java注解与自定义注解
    mysql联合索引的生效规则
    Maven常用命令及其作用、常见问题、常用命令使用场景举例
    反射的理解
    VirtualBox创建centos
  • 原文地址:https://www.cnblogs.com/buerjj/p/14768065.html
Copyright © 2011-2022 走看看