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
void *objc_destructInstance(id obj)
{
if (obj) {
Class isa_gen = _object_getClass(obj);
class_t *isa = newcls(isa_gen); // Read all of the flags at once for performance.
bool cxx = hasCxxStructors(isa);
bool assoc = !UseGC && _class_instancesHaveAssociatedObjects(isa_gen); // This order is important.
if (cxx) object_cxxDestruct(obj);
if (assoc) _object_remove_assocations(obj); if (!UseGC) objc_clear_deallocating(obj);
} return obj;
}
void objc_clear_deallocating(id obj)
{
assert(obj);
assert(!UseGC); SideTable *table = SideTable::tableForPointer(obj); /* *** THIS LINE *** */ // clear any weak table items
// clear extra retain count and deallocating bit
// (fixme warn or abort if extra retain count == 0 ?)
OSSpinLockLock(&table->slock);
if (seen_weak_refs) {
arr_clear_deallocating(&table->weak_table, obj); /* *** THIS LINE *** */
}
table->refcnts.erase(DISGUISE(obj)); /* *** THIS LINE *** */
OSSpinLockUnlock(&table->slock);
}
在上面的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://*.com/questions/30673101/is-it-safe-to-read-a-weak-pointer-while-its-being-deallocated
http://*.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/