iOS13下 'threading violation: expected the main thread' Crash分析及解决

问题描述
iOS13及以上的系统,使用Xcode11.2编译器运行,在特定的路径下唤起系统拍照/录像会直接Crash,使用该Demo的Crash的日志如下:


2019-12-24 10:28:40.709607+0800 HDCameraCrashDemo[3338:1286515] *** Assertion failure in -[FBSSerialQueue assertOnQueue], /BuildRoot/Library/Caches/com.apple.xbs/Sources/FrontBoardServices/FrontBoard-626.4.1/FrontBoardServices/FBSSerialQueue.m:98

2019-12-24 10:28:40.709836+0800 HDCameraCrashDemo[3338:1286515] *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'threading violation: expected the main thread'

*** First throw call stack:

(0x1beebc96c 0x1bebd5028 0x1bedb94fc 0x1bf1fa700 0x1c40eb7ec 0x1c409d460 0x1c409d6ec 0x1c409d5e4 0x1c2b1f120 0x1c2c0ed50 0x1c2c0fb20 0x1e13c3514 0x1bebef3f8 0x1e1466118 0x1bebd4130 0x1bebe6f80 0x1bebede44 0x1e145b2c4 0x1bebef3f8 0x1bee0243c 0x1bed8cfc0 0x1bebef3f8 0x1e14be704 0x1bebd4130 0x1bebe6f80 0x1bebede44 0x1e14af8ac 0x1bebef3f8 0x1beadfa08 0x1bebef3f8 0x1beadfa08 0x1004ef27c 0x1004f690c 0x1004f74fc 0x1005024dc 0x1bebc76d0 0x1bebcd9e8)

libc++abi.dylib: terminating with uncaught exception of type NSException

(lldb) bt

* thread #4, queue = 'com.apple.camera.capture-engine.session-queue', stop reason = signal SIGABRT

  * frame #0: 0x00000001beca6efc libsystem_kernel.dylib`__pthread_kill + 8

    frame #1: 0x00000001bebc68b8 libsystem_pthread.dylib`pthread_kill + 228

    frame #2: 0x00000001beb56a74 libsystem_c.dylib`abort + 104

    frame #3: 0x00000001bec6e3c8 libc++abi.dylib`abort_message + 132

    frame #4: 0x00000001bec6e5c0 libc++abi.dylib`demangling_terminate_handler() + 308

    frame #5: 0x00000001bebd5308 libobjc.A.dylib`_objc_terminate() + 124

    frame #6: 0x00000001bec7b634 libc++abi.dylib`std::__terminate(void (*)()) + 20

    frame #7: 0x00000001bec7b5c0 libc++abi.dylib`std::terminate() + 44

    frame #8: 0x00000001bebd528c libobjc.A.dylib`objc_terminate + 16

    frame #9: 0x00000001004ef290 libdispatch.dylib`_dispatch_client_callout + 40

    frame #10: 0x00000001004f690c libdispatch.dylib`_dispatch_lane_serial_drain + 720

    frame #11: 0x00000001004f74fc libdispatch.dylib`_dispatch_lane_invoke + 408

    frame #12: 0x00000001005024dc libdispatch.dylib`_dispatch_workloop_worker_thread + 1344

    frame #13: 0x00000001bebc76d0 libsystem_pthread.dylib`_pthread_wqthread + 280

(lldb)

在实际项目中的Crash日志及堆栈信息如下:


* thread #67, queue = 'com.apple.camera.capture-engine.session-queue', stop reason = breakpoint 3.1

    frame #0: 0x00000001bebd4fec libobjc.A.dylib`objc_exception_throw

    frame #1: 0x00000001bedb94fc CoreFoundation`+[NSException raise:format:arguments:] + 100

    frame #2: 0x00000001bf1fa700 Foundation`-[NSAssertionHandler handleFailureInMethod:object:file:lineNumber:description:] + 132

    frame #3: 0x00000001c40eb7ec FrontBoardServices`-[FBSSerialQueue assertOnQueue] + 236

    frame #4: 0x00000001c409d460 FrontBoardServices`-[FBSSceneImpl updateClientSettings:withTransitionContext:] + 80

    frame #5: 0x00000001c409d6ec FrontBoardServices`-[FBSSceneImpl updateClientSettingsWithTransitionBlock:] + 168

    frame #6: 0x00000001c409d5e4 FrontBoardServices`-[FBSSceneImpl updateClientSettingsWithBlock:] + 128

    frame #7: 0x00000001c2b1f120 UIKitCore`-[FBSScene(UIApp) updateUIClientSettingsWithBlock:] + 184

    frame #8: 0x00000001c2c0ed50 UIKitCore`-[UIDevice(Private) _enableDeviceOrientationEvents:] + 156

  * frame #9: 0x00000001c2c0fb20 UIKitCore`-[UIDevice endGeneratingDeviceOrientationNotifications] + 60

    frame #10: 0x00000001e13c3514 CameraUI`-[CAMMotionController dealloc] + 68

    frame #11: 0x00000001bebef3f8 libobjc.A.dylib`objc_release + 136

    frame #12: 0x00000001e1466118 CameraUI`-[CUCaptureController .cxx_destruct] + 92

    frame #13: 0x00000001bebd4130 libobjc.A.dylib`object_cxxDestructFromClass(objc_object*, objc_class*) + 116

    frame #14: 0x00000001bebe6f80 libobjc.A.dylib`objc_destructInstance + 92

    frame #15: 0x00000001bebede44 libobjc.A.dylib`_objc_rootDealloc + 52

    frame #16: 0x00000001e145b2c4 CameraUI`-[CUCaptureController dealloc] + 120

    frame #17: 0x00000001bebef3f8 libobjc.A.dylib`objc_release + 136

    frame #18: 0x00000001bee0243c CoreFoundation`__RELEASE_OBJECTS_IN_THE_ARRAY__ + 116

    frame #19: 0x00000001bed8cfc0 CoreFoundation`-[__NSArrayM dealloc] + 172

    frame #20: 0x00000001bebef3f8 libobjc.A.dylib`objc_release + 136

    frame #21: 0x00000001e14be704 CameraUI`-[CAMCaptureEngine .cxx_destruct] + 176

    frame #22: 0x00000001bebd4130 libobjc.A.dylib`object_cxxDestructFromClass(objc_object*, objc_class*) + 116

    frame #23: 0x00000001bebe6f80 libobjc.A.dylib`objc_destructInstance + 92

    frame #24: 0x00000001bebede44 libobjc.A.dylib`_objc_rootDealloc + 52

    frame #25: 0x00000001e14af8ac CameraUI`-[CAMCaptureEngine dealloc] + 168

    frame #26: 0x00000001bebef3f8 libobjc.A.dylib`objc_release + 136

    frame #27: 0x00000001beadfa08 libsystem_blocks.dylib`_Block_release + 168

    frame #28: 0x00000001bebef3f8 libobjc.A.dylib`objc_release + 136

    frame #29: 0x00000001beadfa08 libsystem_blocks.dylib`_Block_release + 168

    frame #30: 0x00000001088ff27c libdispatch.dylib`_dispatch_client_callout + 20

    frame #31: 0x000000010890690c libdispatch.dylib`_dispatch_lane_serial_drain + 720

    frame #32: 0x00000001089074fc libdispatch.dylib`_dispatch_lane_invoke + 408

    frame #33: 0x00000001089124dc libdispatch.dylib`_dispatch_workloop_worker_thread + 1344

    frame #34: 0x00000001bebc76d0 libsystem_pthread.dylib`_pthread_wqthread + 280

<video src="./HDCameraCrashDemo.mp4" controls="true" />

问题分析

在iOS13中,系统对在子线程进行UI操作做了更加严格的检验,会直接抛出 threading violation: expected the main thread 。该问题在真实项目中,我们对堆栈信息中的 [UIDevice endGeneratingDeviceOrientationNotifications] 进行 hook 处理,通过 Crash必现的路径,看到这个方法会发生在子线程中:


@implementation UIDevice (PG)

static inline void pg_swizzleSelector(Class theClass, SEL originalSelector, SEL swizzledSelector) {

    Method originalMethod = class_getInstanceMethod(theClass, originalSelector);

    Method swizzledMethod = class_getInstanceMethod(theClass, swizzledSelector);

    method_exchangeImplementations(originalMethod, swizzledMethod);

}

+ (void)load {

    if ([UIDevice currentDevice].systemVersion.floatValue >= 13.0) {

        pg_swizzleSelector(UIDevice.class, @selector(endGeneratingDeviceOrientationNotifications), @selector(pgEndGeneratingDeviceOrientationNotifications));

    }

}

- (void)pgEndGeneratingDeviceOrientationNotifications {

    NSLog(@"pgEndGeneratingDeviceOrientationNotifications isMainThread:%d", [NSThread isMainThread]);

    [self pgEndGeneratingDeviceOrientationNotifications];

}

@end



pgEndGeneratingDeviceOrientationNotifications isMainThread:0

也就是说在子线程中,触发了该方法,然后系统监听该通知做了UI操作,然后导致的Crash

问题解决

hook [UIDevice endGeneratingDeviceOrientationNotifications] 判断执行该方法是否在主线程中执行,如果不是,则同步到主线程中转发: (最终代码)


@implementation UIDevice (PG)

static inline void pg_swizzleSelector(Class theClass, SEL originalSelector, SEL swizzledSelector) {

    Method originalMethod = class_getInstanceMethod(theClass, originalSelector);

    Method swizzledMethod = class_getInstanceMethod(theClass, swizzledSelector);

    method_exchangeImplementations(originalMethod, swizzledMethod);

}

+ (void)load {

    if ([UIDevice currentDevice].systemVersion.floatValue >= 13.0) {

        pg_swizzleSelector(UIDevice.class, @selector(endGeneratingDeviceOrientationNotifications), @selector(pgEndGeneratingDeviceOrientationNotifications));

    }

}

- (void)pgEndGeneratingDeviceOrientationNotifications {
    NSLog(@"pgEndGeneratingDeviceOrientationNotifications isMainThread:%d", [NSThread isMainThread]);
    if (![NSThread isMainThread]) {
        dispatch_async(dispatch_get_main_queue(), ^{
            [self pgEndGeneratingDeviceOrientationNotifications];
        });
        return;
    }
    [self pgEndGeneratingDeviceOrientationNotifications];
}

@end

问题结束了?

上面的问题解决,其实的确是能解决问题,但是并没有从根源上发现到底是什么地方导致,通过查看代码发现,我们的项目中对 [[UIDevice currentDevice] endGeneratingDeviceOrientationNotifications][[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications] 没有成对实现,测试发现,如果 多调用了两次 [[UIDevice currentDevice] endGeneratingDeviceOrientationNotifications] , 然后再唤起H5的拍照/录视频,在iOS13系统上必然Crash,可以在下载 HDCameraCrashDemo 进行验证

可以在 [[UIDevice currentDevice] endGeneratingDeviceOrientationNotifications] 、[[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications] 添加一个 BOOL 类型的变量来控制他们的成对出现,从根本上解决这类问题。

所以这个并不一定是iOS13系统的问题,只要在调用系统方法合理,并不会有该类型的Crash发生。

最后编辑于
?著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,992评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,212评论 3 388
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事?!?“怎么了?”我有些...
    开封第一讲书人阅读 159,535评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,197评论 1 287
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,310评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,383评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,409评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,191评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,621评论 1 306
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,910评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,084评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,763评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,403评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,083评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,318评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,946评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,967评论 2 351

推荐阅读更多精彩内容

  • Swift1> Swift和OC的区别1.1> Swift没有地址/指针的概念1.2> 泛型1.3> 类型严谨 对...
    cosWriter阅读 11,094评论 1 32
  • Swift Packages 目前Pod跟SPM的兼容还没做好,配置好SPM后,Pod不能进行正常更新,先配置好P...
    MMOTE阅读 1,055评论 0 2
  • 首先手机系统升级为iOS13,相应的xcode的版本要升级为11 注意: 使用xcode 10 编译的APP 在i...
    若水water阅读 4,800评论 2 16
  • 转摘自微信读书 在实现需求的同时,能写出既优雅性能又高效的代码是每个开发者都在追求的目标,但是在实际开发中,随着每...
    Arthurcsh阅读 837评论 0 1
  • 古人常云:“一张一弛,文武之道”,在学习英语的过程中,穿插一下简笔画的学习会有助于这两项技能的双提升! 昨天,同事...
    刚刚的钢阅读 641评论 0 0