signed

QiShunwang

“诚信为本、客户至上”

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

2021/3/21 0:18:05   来源:

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

按道理-[__NSDictionaryM objectForKey:] 即便dict是nil或者key是nil都不会crash啊。

查阅资料:有同学反馈 NSMutableDictionary is not thread safe。先在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];
    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];
}

将这个代码放到demo中。必现crash。比较有意思的是:循环10000次才必现一次crash。

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

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

特此记录一下。