主线程 -[__NSDictionaryM objectForKey:] SEGV_ACCERR 类型的crash

Posted hherima

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了主线程 -[__NSDictionaryM objectForKey:] SEGV_ACCERR 类型的crash相关的知识,希望对你有一定的参考价值。

项目中遇到一个偶现crash,如图:

按道理-[__NSDictionaryM objectForKey:] 即便dict是nil或者key是nil都不会crash啊。况且还在主线程,测试过程中并没有crash。

查阅资料后:Stack Overflow上有同学反馈 NSMutableDictionary is not thread safe。所以先搞一个demo,在demo中模拟一下场景:

+ (NSMutableDictionary *)cacheDictionary
    static NSMutableDictionary *instance = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^
        instance = [[NSMutableDictionary alloc] init];
    );
    return instance;

+ (NSString *)openUDID
    NSMutableDictionary *dict = [self cacheDictionary];
//    NSMutableDictionary *dict = [[self cacheDictionary] copy];//修复后代码
    NSString *openUDID = nil;
    int i = 0;
    while (i<10000) 
        openUDID = [dict objectForKey:@"kSTAD_OPENUDID_CACHEKEY"];
//            NSLog(@"openUDID = %@",openUDID);
        i++;
    
    return openUDID;


- (void)viewDidLoad 
    [super viewDidLoad];
    // Do any additional setup after loading the view.
    self.view.backgroundColor = UIColor.greenColor;
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^
        NSMutableDictionary *dict = nil;
        dict = [ViewController cacheDictionary];
        int i=0;
        while (i<10000) 
            [dict setObject:@"1" forKey:@"kSTAD_OPENUDID_CACHEKEY"];
            [dict removeAllObjects];
            [dict setObject:@"2" forKey:@"kSTAD_OPENUDID_CACHEKEY"];
            [dict removeAllObjects];
            [dict setObject:@"3" forKey:@"kSTAD_OPENUDID_CACHEKEY"];
            [dict objectForKey:@"kSTAD_OPENUDID_CACHEKEY"];
            [dict removeAllObjects];
            [dict setObject:@"4" forKey:@"kSTAD_OPENUDID_CACHEKEY"];
            i++;
        
    );
    [ViewController openUDID];

上面代码,必现crash。crash信息和bugly上的一样,简直一模一样。

调试时候提示如下信息:

Thread1:EXC_BAD_ACCESS(code=1,address=0x0)

比较有意思的是:循环10000次才必现一次crash,小数据量循环不会crash

Stack Overflow网友上说的,在+ (NSString *)openUDID方法中操作dict的地方添加@synchronized (self)  ,但是这个锁,这我这个case里并不好使。

分析后发现:后台线程和主线程都只对dict进行操作了。并不是对openUDID方法操作。所以需要对 [self cacheDictionary]进行copy操作。代码就运营正常了。

特此记录一下。

 

 

 

 

 

 

 

 

 

 

以上是关于主线程 -[__NSDictionaryM objectForKey:] SEGV_ACCERR 类型的crash的主要内容,如果未能解决你的问题,请参考以下文章

QThread 通信线程安全

NSDictionaryM setObject:forKey: EXC_BAD_ACCESS 崩溃

Python学习之路:守护线程

ThreadPool

Android多线程的四种方式:Handler、AsyncTask、ThreadPoolExector、IntentService

线程中释放锁的方式