一个iOS11上的列表必现crash

2022-12-01 10:45:00 浏览数 (2)

一个只在iOS11上才发生的crash,crash日志在附件。经过排查后发现是在引导关注弹幕出现的时候退出才会必现crash,之后进行了复现,拿到了完整的crash堆栈。

代码语言:objective-c复制
    * thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=EXC_I386_GPFLT)
    frame #0: 0x000000011715398b libobjc.A.dylib`objc_msgSend   11
    frame #1: 0x000000010dbcd0b9 Foundation` [NSLayoutConstraint _findCommonAncestorOfItem:andItem:]   140
    frame #2: 0x000000010ddd2f39 Foundation`NSLayoutConstraintIsDanglyInContainer   134
    frame #3: 0x000000011199e6b7 UIKit`_UIViewRemoveConstraintsMadeDanglyByChangingSuperview   1041
    frame #4: 0x0000000110ee205a UIKit`__45-[UIView(Hierarchy) _postMovedFromSuperview:]_block_invoke   61
    frame #5: 0x0000000110ee1f8a UIKit`-[UIView(Hierarchy) _postMovedFromSuperview:]   808
    frame #6: 0x0000000110edfe3e UIKit`__UIViewWasRemovedFromSuperview   169
    frame #7: 0x0000000110edf930 UIKit`-[UIView(Hierarchy) removeFromSuperview]   479
    frame #8: 0x0000000110ec8c07 UIKit`-[UIView dealloc]   508
    frame #9: 0x000000011157bcaf UIKit`-[UITableViewCellContentView dealloc]   68
    frame #10: 0x000000011126c308 UIKit`-[UITableViewCell .cxx_destruct]   706
    frame #11: 0x000000011713b920 libobjc.A.dylib`object_cxxDestructFromClass(objc_object*, objc_class*)   127
    frame #12: 0x0000000117147502 libobjc.A.dylib`objc_destructInstance   124
    frame #13: 0x0000000117147539 libobjc.A.dylib`object_dispose   22
    frame #14: 0x00000001110575c6 UIKit`-[UIResponder dealloc]   145
    frame #15: 0x0000000110ec8e04 UIKit`-[UIView dealloc]   1017
    frame #16: 0x0000000111259800 UIKit`-[UITableViewCell dealloc]   316
    frame #17: 0x000000010c69246d CoreFoundation`-[__NSArrayM dealloc]   157
    frame #18: 0x0000000117151a6e libobjc.A.dylib`objc_object::sidetable_release(bool)   202
    frame #19: 0x0000000110f97482 UIKit`-[UITableView .cxx_destruct]   1561
    frame #20: 0x000000011713b920 libobjc.A.dylib`object_cxxDestructFromClass(objc_object*, objc_class*)   127
    frame #21: 0x0000000117147502 libobjc.A.dylib`objc_destructInstance   124
    frame #22: 0x0000000117147539 libobjc.A.dylib`object_dispose   22
    frame #23: 0x00000001110575c6 UIKit`-[UIResponder dealloc]   145
    frame #24: 0x0000000110ec8e04 UIKit`-[UIView dealloc]   1017
    frame #25: 0x0000000110f039c7 UIKit`-[UIScrollView dealloc]   816
    frame #26: 0x0000000110f5d990 UIKit`-[UITableView dealloc]   473
    frame #27: 0x00000001050d64b0 QGame`-[DMKPanel .cxx_destruct](self=0x00007fb4624890a0, _cmd=<no value available>) at DMKPanel.m:53:17
    frame #28: 0x000000011713b920 libobjc.A.dylib`object_cxxDestructFromClass(objc_object*, objc_class*)   127
    frame #29: 0x0000000117147502 libobjc.A.dylib`objc_destructInstance   124
    frame #30: 0x0000000117147539 libobjc.A.dylib`object_dispose   22
    frame #31: 0x00000001110575c6 UIKit`-[UIResponder dealloc]   145
    frame #32: 0x0000000110ec8e04 UIKit`-[UIView dealloc]   1017
    frame #33: 0x00000001171521b2 libobjc.A.dylib`(anonymous namespace)::AutoreleasePoolPage::pop(void*)   860
    frame #34: 0x0000000110ec8de3 UIKit`-[UIView dealloc]   984
    frame #35: 0x0000000107455a62 QGame`-[QGPortraitFullDanmakuController .cxx_destruct](self=0x00006080006bce60, _cmd=<no value available>) at QGPortraitFullDanmakuController.m:47:17
    frame #36: 0x000000011713b920 libobjc.A.dylib`object_cxxDestructFromClass(objc_object*, objc_class*)   127
    frame #37: 0x0000000117147502 libobjc.A.dylib`objc_destructInstance   124
    frame #38: 0x0000000117147539 libobjc.A.dylib`object_dispose   22
    frame #39: 0x0000000107451ec5 QGame`-[QGPortraitFullDanmakuController dealloc](self=0x00006080006bce60, _cmd="dealloc") at QGPortraitFullDanmakuController.m:107:1
    frame #40: 0x0000000117151a6e libobjc.A.dylib`objc_object::sidetable_release(bool)   202
    frame #41: 0x000000010c69246d CoreFoundation`-[__NSArrayM dealloc]   157
    frame #42: 0x0000000117151a6e libobjc.A.dylib`objc_object::sidetable_release(bool)   202
    frame #43: 0x0000000105135e5e QGame`-[QGSafeMutableArray .cxx_destruct](self=0x000060c000a20880, _cmd=".cxx_destruct") at QGSafeMutableArray.m:31:17
    frame #44: 0x000000011713b920 libobjc.A.dylib`object_cxxDestructFromClass(objc_object*, objc_class*)   127
    frame #45: 0x0000000117147502 libobjc.A.dylib`objc_destructInstance   124
    frame #46: 0x0000000117147539 libobjc.A.dylib`object_dispose   22
    frame #47: 0x0000000117151a6e libobjc.A.dylib`objc_object::sidetable_release(bool)   202
    frame #48: 0x0000000106ab01f2 QGame`-[QGLiveDetailCommunicationCenter removeBusinessControllers:](self=0x00006040008254a0, _cmd="removeBusinessControllers:", businessControllers=0x0000604000a21c20) at QGLiveDetailCommunicationCenter.m:61:1
    frame #49: 0x0000000106ab02f2 QGame`-[QGLiveDetailCommunicationCenter cleanBusinessControllers](self=0x00006040008254a0, _cmd="cleanBusinessControllers") at QGLiveDetailCommunicationCenter.m:64:5
    frame #50: 0x00000001067db04a QGame`-[QGVoiceRoomViewController dealloc](self=0x00007fb4623f68c0, _cmd="dealloc") at QGVoiceRoomViewController.m:111:5
    frame #51: 0x0000000106488128 QGame`-[QGLiveVCContainer .cxx_destruct](self=0x00007fb4623dc910, _cmd=".cxx_destruct") at QGLiveVCContainer.m:36:17
    frame #52: 0x000000011713b920 libobjc.A.dylib`object_cxxDestructFromClass(objc_object*, objc_class*)   127
    frame #53: 0x0000000117147502 libobjc.A.dylib`objc_destructInstance   124
    frame #54: 0x0000000117147539 libobjc.A.dylib`object_dispose   22
    frame #55: 0x00000001110575c6 UIKit`-[UIResponder dealloc]   145
    frame #56: 0x0000000110fca8f2 UIKit`-[UIViewController dealloc]   1807
    frame #57: 0x0000000106f6550d QGame`-[UIViewController(self=0x00007fb4623dc910, _cmd="dealloc") grayUpgradeAlert] at UIViewController GrayUpgrade.m:128:5
    frame #58: 0x00000001068d4ad5 QGame`-[QGViewController dealloc](self=0x00007fb4623dc910, _cmd="dealloc") at QGViewController.m:231:1
    frame #59: 0x00000001064851df QGame`-[QGLiveVCContainer dealloc](self=0x00007fb4623dc910, _cmd="dealloc") at QGLiveVCContainer.m:119:1
    frame #60: 0x0000000111001252 UIKit`__destroy_helper_block_.859   38
    frame #61: 0x0000000118c6398a libsystem_blocks.dylib`_Block_release   111
    frame #62: 0x0000000111b83fe9 UIKit`-[_UIViewControllerTransitionContext .cxx_destruct]   94
    frame #63: 0x000000011713b920 libobjc.A.dylib`object_cxxDestructFromClass(objc_object*, objc_class*)   127
    frame #64: 0x0000000117147502 libobjc.A.dylib`objc_destructInstance   124
    frame #65: 0x0000000117147539 libobjc.A.dylib`object_dispose   22
    frame #66: 0x0000000111b8282e UIKit`-[_UIViewControllerTransitionContext dealloc]   55
    frame #67: 0x0000000111b8408e UIKit`-[_UIViewControllerOneToOneTransitionContext dealloc]   78
    frame #68: 0x0000000117151a6e libobjc.A.dylib`objc_object::sidetable_release(bool)   202
    frame #69: 0x0000000105096e9d QGame`__destroy_helper_block_e8_32s((null)=0x000060800024fd80) at DMKAchievementMedalMaterialGen.m:39:28
    frame #70: 0x0000000118c6398a libsystem_blocks.dylib`_Block_release   111
    frame #71: 0x0000000110eec1e5 UIKit`-[UIViewAnimationBlockDelegate .cxx_destruct]   58
    frame #72: 0x000000011713b920 libobjc.A.dylib`object_cxxDestructFromClass(objc_object*, objc_class*)   127
    frame #73: 0x0000000117147502 libobjc.A.dylib`objc_destructInstance   124
    frame #74: 0x0000000117147539 libobjc.A.dylib`object_dispose   22
    frame #75: 0x0000000117151a6e libobjc.A.dylib`objc_object::sidetable_release(bool)   202
    frame #76: 0x000000010c6c364d CoreFoundation`-[__NSDictionaryI dealloc]   125
    frame #77: 0x0000000117151a6e libobjc.A.dylib`objc_object::sidetable_release(bool)   202
    frame #78: 0x00000001171521b2 libobjc.A.dylib`(anonymous namespace)::AutoreleasePoolPage::pop(void*)   860
    frame #79: 0x000000010c6aa136 CoreFoundation`_CFAutoreleasePoolPop   22
    frame #80: 0x000000010c6e6eae CoreFoundation`__CFRunLoopRun   2350
    frame #81: 0x000000010c6e630b CoreFoundation`CFRunLoopRunSpecific   635
    frame #82: 0x0000000119959a73 GraphicsServices`GSEventRunModal   62
    frame #83: 0x0000000110e2a057 UIKit`UIApplicationMain   159
    frame #84: 0x0000000106c38b84 QGame`main(argc=1, argv=0x00007ffeeab6a3d8) at main.m:46:16
    frame #85: 0x0000000118bf2955 libdyld.dylib`start   1
    frame #86: 0x0000000118bf2955 libdyld.dylib`start   1

如堆栈所示,crash发生在弹幕面板dealloc的时候,当UITableView进行dealloc时,会对所有的cell进行dealloc,cell的subview会执行removeFromSuperView方法,最后crash在[NSLayoutConstraint _findCommonAncestorOfItem:andItem:],从方法名上可以看到应该是因为某两个subview的约束出现了问题,这两个item有一个commonAncestor导致了crash,虽然不太明确为什么iOS11上两个subview有commonAncestor时dealloc会crash,但检查代码和当版本需求后确认是DMKLocalFocusDanmuCell引入的。

首先描述下引导关注弹幕的特殊逻辑,需求要求首先在弹幕面板上方展示引导关注按钮,可以有正常关注逻辑,之后展示3秒后,引导关注按钮转为一条特殊弹幕,同样有正常关注逻辑,并且即使此弹幕划走之后,关注状态依然会有通知进行相应。因此为了达到逻辑复用和对弹幕面板侵入性最少,选择了新建DanmuCell并在config时将引导关注View贴在弹幕cell上,但同时UITableView中的cell是会回收的,为了保证关注view能响应通知,因此使用了model层来持有了这个view,也为之后的crash埋下了隐患。

修复方案,在danmuCell的dealloc方法里手动讲引导关注按钮removeFromSuperView,并将danmuCell中的指针置为nil,这样保证了cell在dealloc时不再需要处理引导关注按钮。

代码语言:objective-c复制
- (void)configureWithLayout:(DMKItemLayout *)layout {
    [super configureWithLayout:layout];
    if (layout.focusDanmuView == nil) {
        [layout configFocusView];
        
    }
    [self.focusDanmuView removeFromSuperview];
    [layout.focusDanmuView removeFromSuperview];
    self.focusDanmuView = layout.focusDanmuView;
    [self.contentView removeAllSubviews];
    [self.contentView addSubview:self.focusDanmuView];
    [self.focusDanmuView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.centerY.equalTo(self.focusDanmuView.superview);
        make.left.equalTo(self.focusDanmuView.superview).offset(10);
    }];
}
// iOS 11必须手动移除subview 不然会crash
- (void)dealloc {
    [self.focusDanmuView removeFromSuperview];
    self.focusDanmuView = nil;
}

0 人点赞