在dealloc方法中,如下代码就会crash:
- (void)dealloc
{
__weak __typeof(self)weak_self = self;
NSLog(@"%@", weak_self);
}
控制台报错内容如下:
objc[4572]: Cannot form weak reference to instance (0x160f6f890) of class XXX. It is possible that this object was over-released, or is in the process of deallocation.
原因如下:__weak 会调用weak_register_no_lock,里边有对于当前实例是否处在deallocating状态(正在被释放的状态)的判断,如果处在deallocating状态,则crash。
id
weak_register_no_lock(weak_table_t *weak_table, id referent_id, id *referrer_id)
{
objc_object *referent = (objc_object *)referent_id;
objc_object **referrer = (objc_object **)referrer_id;
if (!referent || referent->isTaggedPointer()) return referent_id;
// ensure that the referenced object is viable
bool deallocating;
if (!referent->ISA()->hasCustomRR()) {
deallocating = referent->rootIsDeallocating();
}
else {
BOOL (*allowsWeakReference)(objc_object *, SEL) =
(BOOL(*)(objc_object *, SEL))
object_getMethodImplementation((id)referent,
SEL_allowsWeakReference);
if ((IMP)allowsWeakReference == _objc_msgForward) {
return nil;
}
deallocating =
! (*allowsWeakReference)(referent, SEL_allowsWeakReference);
}
if (deallocating) {
_objc_fatal("Cannot form weak reference to instance (%p) of "
"class %s. It is possible that this object was "
"over-released, or is in the process of deallocation.",
(void*)referent, object_getClassName((id)referent));
}
// now remember it and where it is being stored
weak_entry_t *entry;
if ((entry = weak_entry_for_referent(weak_table, referent))) {
append_referrer(entry, referrer);
}
else {
weak_entry_t new_entry;
new_entry.referent = referent;
new_entry.out_of_line = 0;
new_entry.inline_referrers[0] = referrer;
for (size_t i = 1; i < WEAK_INLINE_COUNT; i++) {
new_entry.inline_referrers[i] = nil;
}
weak_grow_maybe(weak_table);
weak_entry_insert(weak_table, &new_entry);
}
// Do not set *referrer. objc_storeWeak() requires that the
// value not change.
return referent_id;
}
2025年4月28号更新:
在dealloc方法里面尽量不要通过self->_xxx这种方式访问成员变量,某些情况下当self为nil的时候,访问会crash,原因如下:
-
self->_xxx
并不是发消息,它是访问内存地址。 -
具体来说,
self->_xxx
会被编译器翻译成:取self
这个指针指向的内存偏移位置的数据。 -
如果
self
是nil
(即地址是 0x0),那么访问self->_xxx
实际上就是访问 0x0 + 偏移的内存。
这个操作会导致——直接崩溃!
因为访问 0x0 地址附近的内存是非法的,会触发 EXC_BAD_ACCESS 错误。