1、在ARC出現之前,Objetive-C的內存管理需要手工執行release&retain操作,這些極大增加了代碼的編寫難度,同時帶來很多的crash。
同時大量的delegate是unretain的,如果忘記在dealloc中主動設置為空,將帶來野指針的隱患。由於dealloc是一個線程不安全的方法
在MRC的環境下面,如果一個對象在一個線程中正在釋放過程當中,這個對象在另外一個線程收到通知,極有可能帶來Crash,
這個取決於執行的方法中訪問的對象內存是否被破壞掉。
2、ARC出現之后帶來了weak修飾符,降低了野指針出現的概率,同時將dealloc方法進行了修改,用戶不需要主動調用[super dealloc],由編譯器插入。
同時編譯器插入了一個cxx_dealloc的方法,這個方法真正釋放該對象持有的變量,dealloc方法只能算作即將釋放的一個回調,那么ARC下面dealloc是怎么執行的呢。
3、dealloc的方法執行
1、一個對象執行release方法,引用計數減少1,變成0
2、調用該對象的dealloc方法
3、該對象dealloc方法執行完畢調用父類的dealloc方法
4、調用NSObject的dealloc方法
5、NSObject的dealloc方法執行_objc_rootDealloc(self);
6、_objc_rootDealloc 中調用 object_dispose()
7、object_dispose 中調用 objc_destructInstance()
8、objc_destructInstance 調用 objc_clear_deallocating
1 void *objc_destructInstance(id obj) 2 { 3 if (obj) { 4 Class isa_gen = _object_getClass(obj); 5 class_t *isa = newcls(isa_gen); 6 7 // Read all of the flags at once for performance. 8 bool cxx = hasCxxStructors(isa); 9 bool assoc = !UseGC && _class_instancesHaveAssociatedObjects(isa_gen); 10 11 // This order is important. 12 if (cxx) object_cxxDestruct(obj); 13 if (assoc) _object_remove_assocations(obj); 14 15 if (!UseGC) objc_clear_deallocating(obj); 16 } 17 18 return obj; 19 } 20 void objc_clear_deallocating(id obj) 21 { 22 assert(obj); 23 assert(!UseGC); 24 25 SideTable *table = SideTable::tableForPointer(obj); /* *** THIS LINE *** */ 26 27 // clear any weak table items 28 // clear extra retain count and deallocating bit 29 // (fixme warn or abort if extra retain count == 0 ?) 30 OSSpinLockLock(&table->slock); 31 if (seen_weak_refs) { 32 arr_clear_deallocating(&table->weak_table, obj); /* *** THIS LINE *** */ 33 } 34 table->refcnts.erase(DISGUISE(obj)); /* *** THIS LINE *** */ 35 OSSpinLockUnlock(&table->slock); 36 }
在上面的objc_destructInstance代碼中,首先釋放object_cxxDestruct方法,這里面會逐級往上移出對象的實例變量
然后移除關聯對象
第三步中清空了weak指針
可以看出來,清除一個對象的實例變量是統一清理的,由下逐級往上。
在清理weak指針的時候如何保證這個對象在dealloc收到消息的時候還是線程安全的呢?
答案下面:
得到一個weak 指針的時候會執行下面的方法,該方法中有一個spinlock,這個鎖在清理weak指針的時候同時會用到,所以是線程安全的。
也就是這個weak指針獲得的結果要么為空,要么不為空,只要不為空,就代表這個指針指向的內存區域沒有被釋放掉,其對象內部的實例變量還是有可能被清理掉的。
這個時候向這個指針發送消息,不會帶來crash,但是邏輯可能異常,這種情況理論上存在。
id objc_loadWeakRetained(id *location) { id result; SideTable *table; spinlock_t *lock; retry: result = *location; if (!result) return nil; table = SideTable::tableForPointer(result); lock = &table->slock; spinlock_lock(lock); if (*location != result) { spinlock_unlock(lock); goto retry; } result = weak_read_no_lock(&table->weak_table, location); spinlock_unlock(lock); return result; }
4、參考資料
http://stackoverflow.com/questions/30673101/is-it-safe-to-read-a-weak-pointer-while-its-being-deallocated
http://stackoverflow.com/questions/14854635/how-can-the-objective-c-runtime-know-whether-a-weakly-referenced-object-is-still/14854977#14854977
http://blog.sunnyxx.com/2014/04/02/objc_dig_arc_dealloc/