Dealloc 时取 weak self 引起崩溃

作者: _Thinking_ | 来源:发表于2016-05-10 18:17 被阅读5947次

    今天无意这中遇到一个奇怪的崩溃,先上引起崩溃的代码:

    - (void)dealloc
    {
        __weak __typeof(self)weak_self = self;
        NSLog(@"%@", weak_self);
    }
    
    

    当执行到dealloc的时候,程序就crash 掉了。
    崩溃信息如下:

    objc[4572]: Cannot form weak reference to instance (0x160f6f890) of class MFChatRoomBoardController. It is possible that this object was over-released, or is in the process of deallocation.
    (lldb) 
    error: empty command
    (lldb) bt
    * thread #1: tid = 0x35914d, 0x0000000182307aac libobjc.A.dylib`_objc_trap(), queue = 'com.apple.main-thread', stop reason = EXC_BREAKPOINT (code=1, subcode=0x182307aac)
      * frame #0: 0x0000000182307aac libobjc.A.dylib`_objc_trap()
        frame #1: 0x0000000182307b24 libobjc.A.dylib`_objc_fatal(char const*, ...) + 88
        frame #2: 0x0000000182319890 libobjc.A.dylib`weak_register_no_lock + 316
        frame #3: 0x0000000182320688 libobjc.A.dylib`objc_initWeak + 224
        frame #4: 0x000000010022bf8c MakeFriends`-[MFChatRoomBoardController dealloc](self=0x0000000160f6f890, _cmd="dealloc") + 36 at MFChatRoomBoardController.m:31
    
    
    

    其中,可以在控制台明确看到这样一段描述:

    objc[4572]: Cannot form weak reference to instance (0x160f6f890) of class MFChatRoomBoardController. It is possible that this object was over-released, or is in the process of deallocation.

    说明不允许在 dealloc 的时候取 weak self.

    查看了一下 weak_register_no_lock 的函数代码,找到问题所在。

    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;
    }
    
    

    可以看出,runtime 是通过检查引用计数的个数来判断对象是否在 deallocting, 然后通过

        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));
        }
    

    这段代码让程序crash。

    再看一下 _objc_fatal 这个函数

    void _objc_fatal(const char *fmt, ...)
    {
        va_list ap; 
        char *buf1;
        char *buf2;
    
        va_start(ap,fmt); 
        vasprintf(&buf1, fmt, ap);
        va_end (ap);
    
        asprintf(&buf2, "objc[%d]: %s\n", getpid(), buf1);
        _objc_syslog(buf2);
        _objc_crashlog(buf2);
    
        _objc_trap();
    }
    

    可以看到这个函数实际会在控制台输出一段信息,然后调用 _bojc_trap() 引起 crash. 而最后一个函数调用刚好也对上我们之前的崩溃堆栈。

    相关文章

      网友评论

      • Detailscool:@Arvin_sir 用KVO吧
        Ray_Wei:@Arvin_sir 我的在_webView.navigationDelegate = self;这行报错,在dealloc把delegate置nil也不行,
        - (void)dealloc {
        self.webView.navigationDelegate = nil;
        self.webView.UIDelegate = nil;
        }
        你是怎么做的?
        吴德馨:@Arvin_sir 感谢!
        4f38d8742e72:解决了, 在dealloc时把delegate置nil即可..
      • Detailscool:WKWebView 懒加载设置其scrollView的delegate也会出现此情况
        DeadRabbit:我也遇到同样的问题~~
        4f38d8742e72:同问, 大哥, 我也遇到了, 这个问题有办法解决吗?
      • HenryPeng:我同作者有一样的错误,但是不知道怎么解决,我没有看懂如何解决这个错误啊?能否请作者详细说明一下呢???
        _Thinking_:@vic虎 就是不能在dealloc时取weak self. 检查一下你的dealloc方法
      • 天口三水羊:有点小问题 在arc中谈引用计数是不是不对:smile:
        _Thinking_:@天口三水羊 ARC只是编译时加了些改变引用计数的代码,实际上对象的生命周期还是通过引用计数来管理的。
      • bigParis:浩哥,居然在这发现你!

      本文标题:Dealloc 时取 weak self 引起崩溃

      本文链接:https://www.haomeiwen.com/subject/gyuyrttx.html