随着上一次解决MJCrash Fix(多线程)问题之后,原本不属于Top1的问题成功晋升成为top1,bugly链接,从bugly看到的奔溃堆栈,发现奔溃的是系统函数的一些调用堆栈,如下图:
直接这样查看,几乎是无从下手,尝试从日志回捞入手,找了几个用户来进入日志回捞,分别是用户ID108975,用户ID1262469,用户ID2320584
从回捞日志中可以看到奔溃的一些信息
-
108975 用户奔溃日志
用户108975 用户奔溃日志.png
-
用户1262469奔溃日志
用户1262469奔溃日志.png
结合bugly的堆栈分析(都是退出直播间奔溃),初步怀疑:主播控件问题,里面有一个removeFromSuperVIew的方法hook了,导致下面代码可能面临问题
///移除所有弹窗
-(void)removeAllDialog{
[self.arrDialog removeAllObjects];
for (UIView * subView in self.subviews) {
[subView removeFromSuperview];//没有被remove掉,因为交换方法之后的dl_removeFromSuperview 什么事情都不做
}
}
//加上日志断点
-(void)socketClassFinishExit:(DLSocketMsgReceiveModel *)model{
if (model.type == socket_biz_subCmd_forceFinish) {
[self dialogSocketTimeOutRoomExit];
}else{
[self dialogSocketClassFinishExit];
}
}
removeFromSuperview被hook掉了,固然[subView removeFromSuperview] 这个代码就是废的,解决方法:不去hook removeFromSuperview这个方法,于是更改直播控件DLLive的代码,去掉hook的代码,然后该功能的修复版本。
经过一段时间的观察,发现该bug已经被修复,layer bounds 奔溃不再存在,所以可以断定是hook removeFromSuperview导致的
bounds1.png bounds2.png
上面提及到初步怀疑的原因分析:
4 UIKitCore -[UIScrollView dealloc] + 104
5 UIKitCore -[UICollectionView dealloc] + 720
6 libobjc.A.dylib __object_remove_assocations + 352
7 libobjc.A.dylib objc_destructInstance + 100
8 libobjc.A.dylib _objc_rootDealloc + 52
9 QuartzCore -[CALayer dealloc] + 188
从堆栈信息里面可以看到是一个对象销毁导致的奔溃,而结合相关源码分析
inline void
objc_object::rootDealloc()
{
if (isTaggedPointer()) return; // fixme necessary?
if (fastpath(isa.nonpointer &&
!isa.weakly_referenced &&
!isa.has_assoc &&
!isa.has_cxx_dtor &&
!isa.has_sidetable_rc))
{
assert(!sidetable_present());
free(this);
}
else {
object_dispose((id)this);
}
}
可以看到是一个对象销毁了,然后销毁过程之后,调用销毁关联对象导致的,然后从日志里面可以看到都是在退出教室之后,就奔溃了,那思路应该就是点击退出教室,然后某对象销毁,调用某对象的associate方法,进而奔溃了,涉及到上课,肯定是和直播控件DLLive有关,而从DLLive的代码里面看,可以看到
associate.png
该DLLiveRoomDialogLayerView里面有showDialogParams:这个方法,
///会马上显示,显示到最上面,加入到数组中,移除时从数组移除
-(void)showDialogParams:(DLLiveRoomDialogReqParams *)reqParams{
NSString * dialogName = [reqParams transformDialogName];
self.hidden = NO;
Class class = NSClassFromString(dialogName);
if (!class) {
class = [DLLiveRoomDialogBaseView class];
}
DLLiveRoomDialogBaseView * dialogView = [[class alloc] initWithReqParams:reqParams delegate:self];
[dialogView showDialog:self];
[self.arrDialog addObject:dialogView];
}
而其中有一个class DLLiveRoomExitClassDialogView 也就是退出课堂的展示视图里面存在一个collectionView,而再结合会堆栈信息,可以比较确定奔溃是在(DLLiveRoomDialogLayerView 和 DLLiveRoomExitClassDialogView 这两个类),分析这两个类的代码,发现可疑的代码,就是removeFromSuperview 被hook了,所以也就是上面为什么将hook去掉,然后测试是否该代码导致,从结果来看正确无疑了。但是至于为什么hook removeFromSuperview 之后不做任何处理会导致奔溃呢?
因为官方没有开源这个方法的源码,从官方文档里面看怀疑是removeFromSuperview里面做了一些系统的操作,导致本该正常的约束流程没有跑完,没有能够得到源码的支持,从官方的解释,应该这个也是比较符合的,removeFromSuperview的官方解释,里面中有一段提及到
If the view’s superview is not nil, the superview releases the view.
Calling this method removes any constraints that refer to the view you are removing, or that refer to any view in the subtree of the view you are removing.
所以有可能是控制器dealloc方法执行后,虽然界面被强制消失,但是removeFromSuperview没有正确执行(通过测试dealloc执行后,removeFromSuperview是会被调用的),所以比较理想的解释应该就是,ViewController对象销毁后,正常是会调用removeFromSuperview执行完接下来的流程的,但是因为被hook掉,没有正确执行(例如XXViewController delegate没有来得及置nil,而对象销毁之后,delegate方法走到了bounds,便出现了crash),所以发生了一些无可预料的奔溃。
test销毁.png
网友评论