weak引用變量是否線程安全


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/

  


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM