一道網易面試題
阿新 • • 發佈:2018-11-09
一、題目描述
題目來自網上一個部落格,具體類似如下
@interface ViewController () @property (nonatomic, strong) NSString *target; @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view, typically from a nib. dispatch_queue_t queue = dispatch_queue_create("parallel", DISPATCH_QUEUE_CONCURRENT); for (int i = 0; i < 1000000000 ; i++) { dispatch_async(queue, ^{ self.target = [NSString stringWithFormat:@"ksddkjalkd2018-11-09 12:04:09.750846+0800 ARCTest2[525:168910] 1111sdsdsjd%d",i]; NSLog(@"%@", self.target); }); } }
問程式碼執行之後會發生什麼?
二、解析
在設定target的setter中,是非執行緒安全的,未加鎖;因此多執行緒訪問這個屬性setter方法的時候潛在crash的情況
因為setter大概如下
- (void)setTarget:(NSString *)target { if(_target != target) { [_target release]; _target = [target retain]; } }
對應runtime程式碼
//objc_class.mm void object_setIvar(id obj, Ivar ivar, id value) { return _object_setIvar(obj, ivar, value, false /*not strong default*/); } static ALWAYS_INLINE void _object_setIvar(id obj, Ivar ivar, id value, bool assumeStrong) { //判斷是否是TaggedPointer if (!obj || !ivar || obj->isTaggedPointer()) return; ptrdiff_t offset; objc_ivar_memory_management_t memoryManagement; //找對應的記憶體管理語義和屬性偏移值 _class_lookUpIvar(obj->ISA(), ivar, offset, memoryManagement); //如果找不到預設是否為Strong,不然為unsafe_unretained if (memoryManagement == objc_ivar_memoryUnknown) { if (assumeStrong) memoryManagement = objc_ivar_memoryStrong; else memoryManagement = objc_ivar_memoryUnretained; } //根據偏移值找到屬性對應位置 id *location = (id *)((char *)obj + offset); //判斷不同的記憶體管理語義,呼叫方法 switch (memoryManagement) { case objc_ivar_memoryWeak: objc_storeWeak(location, value); break; case objc_ivar_memoryStrong: objc_storeStrong(location, value); break; case objc_ivar_memoryUnretained: *location = value; break; case objc_ivar_memoryUnknown: _objc_fatal("impossible"); } }
在release的方法最後會呼叫obj_release
//NSObject.mm void objc_storeStrong(id *location, id obj) { //如果新值指標和舊值一樣,則不更新,直接return id prev = *location; if (obj == prev) { return; } //先對新值retain objc_retain(obj); //再賦值 *location = obj; //最後對舊值release objc_release(prev); }
因為一個物件已經release了,但是這個指標指向的記憶體已經被回收,所以訪問這個指標的記憶體會產生一個記憶體訪問的錯誤
2018-11-09 15:22:04.860819+0800 ARCTest2[93017:2107037] *** -[CFString release]: message sent to deallocated instance 0x600000e70240
以上的程式碼用模擬器是比較容易出現的,因為GCD建立了64個執行緒,執行緒併發次數很多
如果使用iPhoneX的話,沒有出現,(應該是比較難重現),但是存在crash的可能
可以看到GCD建立了6個執行緒,是6核的1倍