今天遇到一个很神奇的事,从原生弹窗->跳转到小程序SDK->然后在小程序SDK里面的webView直接报错了. 查了半天原来是因为deinit里面中self已经在释放中了,不能被弱引用了。下面这个例子就是
protocol MyServiceDelegate: AnyObject {}
class MyService {
weak var delegate: MyServiceDelegate?
func stop() {}
}
class MyClass: NSObject, MyServiceDelegate {
private lazy var service: MyService = {
let service = MyService()
service.delegate = self
return service
}()
deinit {
service.stop()
}
}
// 测试
func test() {
let myClass = MyClass()
}
其实 Crash 信息相对已经比较明显了,结合到代码就是 self 当前已经在释放中了(deinit),不可以被弱引用了(service.delegate = self)。
其实出现这个 Crash 有三个条件:
- lazy
- weak
- NSObject 示例代码去除这三个条件中任何一个,Crash 都不会发生。
一般在dealloc中取weak self会引起这个现象,崩溃的堆栈为:
Thread 0 Crashed:
0 objc_crashlogPKc
1 __objc_fatal
2 _weak_register_no_lock
3 _objc_storeWeak
我们看看将新的weak指针地址添加到弱引用中是如何实现的,即weak_register_no_lock方法
id weak_register_no_lock(weak_table_t *weak_table, id referent_id,
id *referrer_id, bool crashIfDeallocating)
{
// referent_id是新的被弱引用对象
objc_object *referent = (objc_object *)referent_id;
// referrer_id是__weak指针的地址
objc_object **referrer = (objc_object **)referrer_id;
// 如果referent为nil 或 referent 采用了TaggedPointer计数方式,直接返回,不做任何操作
if (!referent || referent->isTaggedPointer()) return referent_id;
// ensure that the referenced object is viable
bool deallocating;
// 确保被引用的对象可用(没有在析构,同时应该支持weak引用)
if (!referent->ISA()->hasCustomRR()) {
deallocating = referent->rootIsDeallocating();
}
else {
BOOL (*allowsWeakReference)(objc_object *, SEL) =
(BOOL(*)(objc_object *, SEL))
object_getMethodImplementation((id)referent,
@selector(allowsWeakReference));
if ((IMP)allowsWeakReference == _objc_msgForward) {
return nil;
}
deallocating =
! (*allowsWeakReference)(referent, @selector(allowsWeakReference));
}
// 正在析构的对象,不能够被弱引用
if (deallocating) {
if (crashIfDeallocating) {
_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));
} else {
return nil;
}
}
// 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(referent, referrer);
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;
}