欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

weak_clear_no_lock 报错处理

程序员文章站 2022-04-19 14:08:42
项目上线之后,通过firbase追踪发现有一个异常报错,闪退率惊人,起初我们以为是firebase同步了WKWebView的异常信息导致的,但后来在本人手机上复测发现实际上不是这个问题,是真正Bug存在。Crashed: com.apple.main-thread0 libsystem_kernel.dylib 0x1af90d858 __abort_with_payload + 81 libsystem_kernel.dylib 0x1af911ed8 abo...

项目上线之后,通过firbase追踪发现有一个异常报错,闪退率惊人,起初我们以为是firebase同步了WKWebView的异常信息导致的,但后来在本人手机上复测发现实际上不是这个问题,是真正Bug存在。

Crashed: com.apple.main-thread
0  libsystem_kernel.dylib         0x1af90d858 __abort_with_payload + 8
1  libsystem_kernel.dylib         0x1af911ed8 abort_with_payload_wrapper_internal + 104
2  libsystem_kernel.dylib         0x1af911e70 abort_with_payload_wrapper_internal + 30
3  libobjc.A.dylib                0x1af85a3cc _objc_fatalv(unsigned long long, unsigned long long, char const*, char*) + 108
4  libobjc.A.dylib                0x1af85a360 _objc_fatalv(unsigned long long, unsigned long long, char const*, char*) + 30
5  libobjc.A.dylib                0x1af856530 weak_clear_no_lock + 322
6  libobjc.A.dylib                0x1af857f70 objc_storeWeak + 336
7  WebKit                         0x1b743d5b0 WebKit::UIDelegate::setDelegate(id<WKUIDelegate>) + 32
8  WebKit                         0x1b71d2acc -[WKWebView setUIDelegate:] + 48
9  XXX                            0x1053e343c -[XXXWebViewController webView] + 426 (XXXWebViewController.m:426)
10 XXX                            0x1053e3318 -[XXXWebViewController dealloc] + 416 (XYFWebViewController.m:416)
11 XXX                            0x1053d97f4 -[XXXManager viewPageChangeWithTarget:actionItem:complete:] + 138 (XXXManager.m:138)
12 XXX                            0x1053d9530 __54-[XXXManager startLoanWithTarget:actionItem:complete:]_block_invoke.115 + 120 (XXXManager.m:120)
13 XXX                            0x1053e6740 __68-[XXXNetClient getUserObjectAfterloginSuccessedWithTarget:callback:]_block_invoke + 232 (XXXNetClient.m:232)
14 libdispatch.dylib              0x1af7c6ec4 _dispatch_call_block_and_release + 32
15 libdispatch.dylib              0x1af7c833c _dispatch_client_callout + 20

看日志是不是以为WKWebView的问题,实际上经过调试发现,是因为当前controller释放的时候,又在dealloc调用了WKWebView设置UIDelegate的方法,但是因为当前controller正在被释放中,所以产生了weak_clear_no_lock报错。报错打印信息如下:

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

场景:登录页面是present出来的,登录完毕后,页面dismiss消失,然后跳转到webView页面,但线上的原因是因为线回调了登录成功block去跳转webview,然后才dismiss页面,由于登录页面没有导航,所以无法跳转webview页面,但是创建了webView页面的controller,dismiss后,刚才创建的webView的controller就要被dealloc,但是在dealloc的时候,在webview的懒加载代理里又设置为代理。此时便会报错。 

//MARK: -- Remove Delegate
- (void)dealloc {
    [self.webView removeObserver:self forKeyPath:@"estimatedProgress"];
    [self.webView removeObserver:self forKeyPath:@"title"];
}
- (WKWebView *)webView {
    if (!_webView) {
        WKWebViewConfiguration *config = [[WKWebViewConfiguration alloc] init];
        _webView = [[WKWebView alloc] initWithFrame:CGRectZero configuration:config];
        _webView.UIDelegate = self;
        _webView.navigationDelegate = self;
        //  设置UserAgent
        [_webView evaluateJavaScript:@"navigator.userAgent" completionHandler:^(id result, NSError *error) {
            NSString *userAgent = result;
            NSString *preStr = [NSString stringWithFormat:@"%@-iOS", [XXXNetClient sharedClient].appname?:@""];
            if (![userAgent hasPrefix:preStr]) {
                NSString *customUserAgent = [preStr stringByAppendingString:userAgent];
                NSDictionary *dictionary = [NSDictionary dictionaryWithObjectsAndKeys:customUserAgent, @"UserAgent", nil];
                [[NSUserDefaults standardUserDefaults] registerDefaults:dictionary];
                [[NSUserDefaults standardUserDefaults] synchronize];
            }
        }];
    }
    return _webView;
}

weak_clear_no_lock是内存管理时释放weak指针清理资源时用到的函数,我们找来它的源码如下:

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, 然后通过deallocting来使得程序报错。问题就是这样,涉及到的Weak的实现方式可以自行了解。

解决方案1:登录页面dismiss之后再回调登录成功的block

解决方案2:   webview的controller在dealloc的时候不使用点语法,可以改为以下方式,以避免程序crash

- (void)dealloc {
    if (_webView) {
        [_webView removeObserver:self forKeyPath:@"estimatedProgress"];
        [_webView removeObserver:self forKeyPath:@"title"];
    }
}

如有错误,烦请指正!

本文地址:https://blog.csdn.net/HelloWorld_1986/article/details/107183215