这周最大的收获是稍稍通透了 多线程安全字典的重要性。
诱因是,发现了有字典坏地址错误
果断以为是 value 或者 key 是可能出现了空值,补充了潜在的判断,虽然有的位置已经预判断的,但是真正赋值的时候并没判断呀,补充上了。
这种问题线下时候,我们基本0复现,所以迭代一个版本用户检验的时候还是报这个错误,不是那种坑坑一直报错势不可挡的bug,但是是很有生命力的bug,不多 还能定位到具体的一行代码。
这就让我很困惑了,怎么回事?? key,value 都保证是合法的了,怎么还是会报错,难道还存在字典也会不合法?
当我把我的疑惑在技术群里抛出,马上就能看出
哪些是酱油水平:“value key你判断了吗 blabla的”。 内心:我代码都粘出去了,为什么还问这个
哪些就是乱支招:“ runtime 进行方法交换呀” 。 内心:我是想解决问题,不是故意屏蔽问题呀,不是你们的项目是不是通通想搞坏的节奏。
哪些是牛逼指点:“数据源放在串行队列,可以保证没问题 ” 内心:竟然看出了,我这个字典保存的对象是网络请求对象。果真透过现象看本质的人。
我也是马上反应过来,这个错误的原因是 多个请求冲突了导致坏地址,需要通过串行队列保证 每次只有一个value key 加入字典 就不会发生野指针。
我也是把我被点得通透的想法问了一下对方,对方也给了肯定的回答。
于是我进一步整理了需要处理的逻辑:
我需要有一个串行请求队列,然后保证在加入字典和移除字典时候 在同一个队列,保证线程的一致。。。
并且迅速查找网上相关资源,如参考1中 中第5和第6部分,
正想根据思路写着,那个小哥哥给我发了一份多线程安全字典文件。
正对着我当前理解的情况,自己写可能得测试调试,这份有份量的文件值得一提的是,小哥哥说是来自于阿里。。。get
以上的工作还是不能够保证字典是线程安全的。。。怎讲?
因为这个字典保存的对象是网络请求对象,除了当前我们设置的串行队列里面持有,网络请求的串行队列里面也会持有这个对象啊,这就是当前问题:资源竞争造成的死锁,野指针也见怪不怪了。
所以还要在字典写入和删除的操作上加锁,保证当前独占资源。
这个加锁的逻辑 学习 参考2
我自己是这么用的 安全锁:
- (void)setOperationDicValue:(id)operation forKey:(NSString *)key { NSCondition *mylock = [[NSCondition alloc]init];//创建锁对象 [mylock lock];//创建锁对象 [self.operationDict setObject:operation forKey:key];//对共享抢占资源进行操作的代码 [mylock unlock];//操作完数据,马上释放锁,给其他的线程调用操作 } - (void)removeOperationObjectForKey:(NSString *)key { NSCondition *mylock = [[NSCondition alloc]init]; [mylock lock]; [self.operationDict removeObjectForKey:key]; [mylock unlock]; } - (void)removeAllOperationObjects { NSCondition *mylock = [[NSCondition alloc]init]; [mylock lock]; [self.operationDict removeAllObjects]; [mylock unlock]; } - (id)getOperationObjectValueForKey:(NSString *)key { return [self.operationDict objectForKey:key]; }
关键怎么让线程不安全的字典变安全:(好东西要分享呀)
// // SyncMutableDictionary.h // banggood // // Created by Artillery on 2017/10/16. // Copyright © 2017年 banggood. All rights reserved. // #import <Foundation/Foundation.h> /* 多线程下的安全字典 来自阿里 */ @interface SyncMutableDictionary : NSObject - (nullable id)objectForKey:(_Nonnull id)aKey; - (nullable id)valueForKey:(_Nonnull id)aKey; - (NSArray * _Nonnull)allKeys; - (void)setObject:(nullable id)anObject forKey:(_Nonnull id <NSCopying>)aKey; - (void)removeObjectForKey:(_Nonnull id)aKey; - (void)removeAllObjects; - (NSMutableDictionary *_Nonnull)getDictionary; @end // // SyncMutableDictionary.m // banggood // // Created by Artillery on 2017/10/16. // Copyright © 2017年 banggood. All rights reserved. // #import "SyncMutableDictionary.h" @interface SyncMutableDictionary () @property(nonatomic, strong) NSMutableDictionary *dictionary; @property(nonatomic, strong) dispatch_queue_t dispatchQueue; @end @implementation SyncMutableDictionary - (instancetype)init { if (self = [super init]) { _dictionary = [NSMutableDictionary new]; _dispatchQueue = dispatch_queue_create("com.banggood.banggoodSycmutableDictionary", DISPATCH_QUEUE_SERIAL); } return self; } - (NSArray * _Nonnull)allKeys{ __block NSArray *allKeys = [NSArray array]; dispatch_sync(_dispatchQueue, ^{ allKeys = [_dictionary allKeys]; }); return allKeys; } - (nullable id)objectForKey:(_Nonnull id)aKey{ __block id returnObject = nil; if(!aKey) return returnObject; dispatch_sync(_dispatchQueue, ^{ returnObject = _dictionary[aKey]; }); return returnObject; } - (void)setValue:(nullable id)value forKey:(NSString *)key { if(!key) return; dispatch_barrier_async(_dispatchQueue, ^{ [_dictionary setValue:value forKey:key]; }); } - (nullable id)valueForKey:(_Nonnull id)aKey{ __block id returnObject = nil; dispatch_sync(_dispatchQueue, ^{ returnObject = [_dictionary valueForKey:aKey]; }); return returnObject; } - (void)setObject:(nullable id)anObject forKey:(_Nonnull id <NSCopying>)aKey{ dispatch_barrier_async(_dispatchQueue, ^{ if (anObject == nil) return; self.dictionary[aKey] = anObject; }); } - (void)removeObjectForKey:(_Nonnull id)aKey{ if(!aKey) return; dispatch_sync(_dispatchQueue, ^{ [_dictionary removeObjectForKey:aKey]; }); } - (void)removeAllObjects { dispatch_sync(_dispatchQueue, ^{ [_dictionary removeAllObjects]; }); } - (NSMutableDictionary *)getDictionary { __block NSMutableDictionary *temp; dispatch_sync(_dispatchQueue, ^{ temp = _dictionary; }); return temp; } -(NSString *)description{ return [NSString stringWithFormat:@"%@",self.dictionary]; } @end
至此总结:
可变字典,(同理可变数组等)是线程不安全的,以后尽量减少在多线程的情况下 处理数据源的情况。
如像我这次这样需要使用的话,处理成多线程安全字典和加安全锁。
参考
1.https://www.cnblogs.com/alunchen/p/5607821.html
2.https://www.cnblogs.com/XYQ-208910/p/4857470.html