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/