一个只在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;
}