iOS 多線程安全 與 可變字典
這周最大的收獲是稍稍通透了 多線程安全字典的重要性。
誘因是,發現了有字典壞地址錯誤

果斷以為是 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.http://www.rzrgm.cn/alunchen/p/5607821.html
2.http://www.rzrgm.cn/XYQ-208910/p/4857470.html
posted on 2018-05-24 16:32 ACM_Someone like you 閱讀(2633) 評論(0) 收藏 舉報
浙公網安備 33010602011771號