作客戶端開發應當時刻考慮多線程問題。我最初是作前端開發的,在這方面考慮得每每不夠。謹記。前端
單例的常見寫法其實就兩種安全
+ (id)sharedInstance { static testClass *sharedInstance = nil; @synchronized(self) { if (!sharedInstance) { sharedInstance = [[self alloc] init]; } } return sharedInstance; }
+ (id)sharedInstance { static testClass *sharedInstance = nil; static dispatch_once_t once; dispatch_once(&once, ^{ sharedInstance = [[self alloc] init]; }); return sharedInstance; }
dispatch_once的寫法更推薦一些。一方面是性能上好一點,另外一方面是語義上更直觀。once,執行一次嘛。網絡
無論是用鎖仍是dispatch_once,本質上都是爲了不單例建立過程出現線程安全問題。多線程
更進一步,咱們常常會有懶加載某些屬性的寫法:框架
- (id<InterfaceEngineA>)engineA { if (_engineA == nil) { _engineA = [EngineA new]; } return _engineA; }
其實跟單例的實現是相似的,這種時候要格外注意線程安全問題。若是存在多線程場景,必定要作好保護性能
- (id<InterfaceEngineA>)engineA { @synchronized(self) { if (_engineA == nil) { _engineA = [EngineA new]; } } return _engineA; }
多線程問題的表現多是各類各樣難以預料的。這裏我遇到的是,_engineA在多線程場景下小几率被重複建立,其實例1在init時註冊了網絡層命令字cmd1
的回包,而這個網絡層框架的實現是,只接受第一個註冊這一命令字的對象。致使實例2註冊失敗。後面調用實例2發送請求,回包都被實例1接收了。從日誌上看,一切都挺正常的。可是下次取數據就是取不到。學習
這個bug第一次提過來的時候,沒分析出根本緣由,只在表面上作了保護。結果第二次提過來才真正改掉。spa
丟人吶。仍是要好好學習纔是。線程