Contents
  1. 1. 崩溃一
  2. 2. 崩溃二
  3. 3. 总结

崩溃一

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
Thread 0 Crashed:
0 libsystem_kernel.dylib __abort_with_payload + 8
1 libsystem_kernel.dylib abort_with_payload_wrapper_internal + 100
2 libsystem_kernel.dylib abort_with_payload_wrapper_internal + 0
3 libobjc.A.dylib _objc_fatalv(unsigned long long, unsigned long long, char const*, char*) + 112
4 libobjc.A.dylib __objc_error + 0
5 libobjc.A.dylib _objc_autoreleasePoolPush + 0
6 CoreFoundation _CFAutoreleasePoolPop + 28
7 UIKitCore _prepareForCAFlush + 132
8 UIKitCore _afterCACommitHandler + 236
9 CoreFoundation __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__ + 32
10 CoreFoundation __CFRunLoopDoObservers + 412
11 CoreFoundation __CFRunLoopRun + 1264
12 CoreFoundation CFRunLoopRunSpecific + 436
13 GraphicsServices GSEventRunModal + 100
14 UIKitCore UIApplicationMain + 212
15 xxxxxxxx main (main.m:36)
16 libdyld.dylib start + 4

分析:
从 CFAutoreleasePoolPop 开始,进入了 error 的函数调用。

在 objc 的源码中找到 objc_fatalv 的实现,这是一个不会返回的函数调用。

static __attribute__((noreturn))
void _objc_fatalv(uint64_t reason, uint64_t flags, const char *fmt, va_list ap)
{
​    char *buf1;
​    vasprintf(&buf1, fmt, ap);
​    char *buf2;
​    asprintf(&buf2, "objc[%d]: %s\n", getpid(), buf1);
​    _objc_syslog(buf2);
​    if (DebugDontCrash) {
​        char *buf3;
​        asprintf(&buf3, "objc[%d]: HALTED\n", getpid());
​        _objc_syslog(buf3);
​        _Exit(1);
​    }
​    else {
​        abort_with_reason(OS_REASON_OBJC, reason, buf1, flags);
​    }
}

abort_with_reason 找不到实现,调用栈最后的abort_with_payload,这个的实现在内核中。

关注到栈上的内存

1
2
3
4
5
"stack@0x16d53a5f8": {
"address": 10795876096,
"type": "string",
"value": "Invalid or prematurely-freed autorelease pool 0x11ec3cef0."
}

有 error 日志的信息,对应起来是 Autorelease 中的内存错误。

static void badPop(void *token)
{
    // Error. For bincompat purposes this is not 
    // fatal in executables built with old SDKs.
    if (DebugPoolAllocation || sdkIsAtLeast(10_12, 10_0, 10_0, 3_0, 2_0)) {
        // OBJC_DEBUG_POOL_ALLOCATION or new SDK. Bad pop is fatal.
        _objc_fatal
            ("Invalid or prematurely-freed autorelease pool %p.", token);
    }
    // Old SDK. Bad pop is warned once.
    static bool complained = false;
    if (!complained) {
        complained = true;
        _objc_inform_now_and_on_crash
            ("Invalid or prematurely-freed autorelease pool %p. "
            "Set a breakpoint on objc_autoreleasePoolInvalid to debug. "
            "Proceeding anyway because the app is old "
            "(SDK version " SDK_FORMAT "). Memory errors are likely.",
                token, FORMAT_SDK(sdkVersion()));
    }
    objc_autoreleasePoolInvalid(token);
}

崩溃二

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
Thread 0 Crashed:
0 libsystem_kernel.dylib __abort_with_payload + 8
1 libsystem_kernel.dylib abort_with_payload_wrapper_internal + 100
2 libsystem_kernel.dylib abort_with_payload_wrapper_internal + 0
3 libobjc.A.dylib _objc_fatalv(unsigned long long, unsigned long long, char const*, char*) + 104
4 libobjc.A.dylib _objc_fatalv(unsigned long long, unsigned long long, char const*, char*) + 0
5 libobjc.A.dylib weak_clear_no_lock + 0
6 libobjc.A.dylib objc_storeWeak + 332
7 UIKitCore -[_UITouchForwardingRecipient initWithResponder:fromResponder:] + 100
8 UIKitCore _UIResponderForwarderWantsForwardingFromResponder + 848
9 UIKitCore -[UITouch _wantsForwardingFromResponder:toNextResponder:withEvent:] + 76
10 UIKitCore __forwardTouchMethod_block_invoke + 48
11 CoreFoundation __NSSET_IS_CALLING_OUT_TO_A_BLOCK__ + 16
12 CoreFoundation -[__NSSetM enumerateObjectsWithOptions:usingBlock:] + 204
13 UIKitCore forwardTouchMethod + 264
14 UIKitCore -[UIResponder touchesBegan:withEvent:] + 60
15 UIKitCore -[UIWindow _sendTouchesForEvent:] + 1692
16 UIKitCore -[UIWindow sendEvent:] + 3352
17 UIKitCore -[UIApplication sendEvent:] + 344
18 UIKitCore __dispatchPreprocessedEventFromEventQueue + 5880
19 UIKitCore __handleEventQueueInternal + 4924
20 UIKitCore __handleHIDEventFetcherDrain + 108
21 CoreFoundation __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ + 24
22 CoreFoundation __CFRunLoopDoSource0 + 80
23 CoreFoundation __CFRunLoopDoSources0 + 260
24 CoreFoundation __CFRunLoopRun + 1080
25 CoreFoundation CFRunLoopRunSpecific + 464
26 GraphicsServices GSEventRunModal + 104
27 UIKitCore UIApplicationMain + 1936
28 xxxxxxxx main (main.m:36)
29 libdyld.dylib start + 4
1
2
3
4
5
6
7
8
9
10
"stack@0x16f391388": {
"address": 4827835744,
"type": "string",
"value": "Cannot form weak reference to instance (0x1249e5000) of class _UIRemoteInputViewController. It is possible that this object was over-released, or is in the process of deallocation."
},
"stack@0x16f391390": {
"address": 8211551800,
"class": "_UIRemoteInputViewController",
"type": "objc_class"
},

简单分析这就是在 UITouch 的一次触摸事件中发生的崩溃,看栈上的内存,是跟 UIRemoteInputViewController 有关。问题根因是对一个对象做 weak 引用时,对象本身已经释放或者正在释放。

id 
weak_register_no_lock(weak_table_t *weak_table, id referent_id, 
​                      id *referrer_id, bool crashIfDeallocating)
{
​    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) {
​        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;
}

简单搜索下关键词 UIRemoteInputViewController,猜测可能是键盘问题。或者第三方键盘问题。第三方键盘老有唤醒失败的情况,闪现一下就没了。对一个正在释放的对象做 weak 引用发生的崩溃。

总结

0   libsystem_kernel.dylib          __abort_with_payload + 8
1   libsystem_kernel.dylib          abort_with_payload_wrapper_internal + 100
2   libsystem_kernel.dylib          abort_with_payload_wrapper_internal + 0
3   libobjc.A.dylib                 _objc_fatalv(unsigned long long, unsigned long long, char const*, char*) + 112
4   libobjc.A.dylib                 __objc_error + 0

abort_with_payload 崩溃的调用栈都应该是相似的,主要看栈上的 error 信息。因为这里的字符串刚巧会在栈上,如果你的崩溃收集器有对栈上的内存做处理收集起来了,那应该多关注这里,这会是突破口。

Contents
  1. 1. 崩溃一
  2. 2. 崩溃二
  3. 3. 总结