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/

  

posted @ 2017-01-11 20:30  兜兜有糖的博客  阅读(2162)  评论(0编辑  收藏  举报