標籤:清空 編譯 and 執行 unlock object_c 使用者 清理 修飾符
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 items28 // clear extra retain count and deallocating bit29 // (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/
weak引用變數是否安全執行緒