一個(gè)線上bug ,一直沒(méi)法重現(xiàn)悠就,但是崩潰率不低,這就問(wèn)題了有點(diǎn)頭疼啦
上述就是具體的崩潰信息充易,原本以為定位這么仔細(xì)就可以立馬找出原因梗脾,然而并沒(méi)有。
這邊用了 DZNEmptyDataSet 和 CHTCollectionViewWaterfallLayout 盹靴,崩潰的點(diǎn)也是在此處出現(xiàn)的炸茧。瑞妇。。
目前沒(méi)法重現(xiàn)這一個(gè) Bug梭冠, 只能通過(guò)圖中返回的情況辕狰,定位到代碼中:
- (CGSize)collectionViewContentSize {
NSInteger numberOfSections = [self.collectionView numberOfSections];
if (numberOfSections == 0) {
return CGSizeZero;
}
CGSize contentSize = self.collectionView.bounds.size;
contentSize.height = [self.columnHeights[0] floatValue];
return contentSize;
}
- (BOOL)dzn_canDisplay {
if (self.emptyDataSetSource && [self.emptyDataSetSource conformsToProtocol:@protocol(DZNEmptyDataSetSource)]) {
if ([self isKindOfClass:[UITableView class]] || [self isKindOfClass:[UICollectionView class]] || [self isKindOfClass:[UIScrollView class]]) {
return YES;
}
}
return NO;
}
可以確定是 崩在 dzn_canDisplay
這里,但是嘗試好多遍都不知道問(wèn)題在哪里...
一控漠、 猜
猜測(cè)蔓倍,是否調(diào)用 [self.collectionView numberOfSections] 的時(shí)候, self.collectionView 的時(shí)候已經(jīng)被提前釋放或者說(shuō)已經(jīng)被干掉啦盐捷。
首先可以肯定的是偶翅,當(dāng) self.collectionView 被干掉此處肯定會(huì)崩,但是此種情況基本不存在碉渡,在 CHTCollectionViewWaterfallLayout 中它是不可被修改的聚谁,而外部的collectionView 又是在生命周期中,不會(huì)被干掉滞诺,所以此處排除垦巴。。铭段。
另外骤宣,如果是 self.collectionView 的問(wèn)題,那么 上述圖中只會(huì)截止崩在該位置序愚,不會(huì)繼續(xù)走 dzn_canDisplay等方法憔披。
二、理一下常見(jiàn)的 Carsh
再判斷爸吮,可以肯定的是什么造成啦 collectionViewContentSize
和 dzn_canDisplay
方法有問(wèn)題芬膝,而又沒(méi)有頭緒...
回過(guò)頭來(lái),先看看分析iOS Crash文件:符號(hào)化iOS Crash文件的3種方法形娇,需要使用Xcode符號(hào)化 crash log锰霜,我們需要下面所列的3個(gè)文件:
- crash報(bào)告(.crash文件)
- 符號(hào)文件 (.dsymb文件)
- 應(yīng)用程序文件 (appName.app文件,把IPA文件后綴改為zip桐早,然后解壓癣缅,Payload目錄下的appName.app文件), 這里的appName是我們的應(yīng)用程序的名稱。
現(xiàn)在關(guān)鍵問(wèn)題哄酝,這bug 根本一直不能在重現(xiàn)友存,可以單純的看到堆棧信息的崩潰日志,再想想一般是什么原因會(huì)造成崩潰陶衅,回顧下 常見(jiàn)的Crash類型:
-
2-1屡立、看門狗
看門狗也就是 Watchdog 機(jī)制,它是iOS為了保持用戶界面的響應(yīng)引入的一種機(jī)制搀军, 膨俐。如果我們的應(yīng)用未能及時(shí)的響應(yīng)一些用戶界面事件勇皇,如啟動(dòng)、暫停焚刺、恢復(fù)和終止敛摘,Watchdog就會(huì)殺死程序并生成一個(gè)Watchdog超時(shí)崩潰報(bào)告。Watchdog超時(shí)時(shí)間并沒(méi)有明文規(guī)定檩坚,但通常會(huì)少于網(wǎng)絡(luò)超時(shí)。(5秒不一定正確)
場(chǎng)景:
- 主線程執(zhí)行同步的網(wǎng)絡(luò)請(qǐng)求诅福,而且請(qǐng)求時(shí)間特別長(zhǎng)匾委。
- 主線程死鎖。
- 長(zhǎng)時(shí)間讀寫(xiě)本地文件
...
// 放在 AppDelegate didFinishLaunchingWithOptions
dispatch_sync(dispatch_get_main_queue(), ^{
NSLog(@"永遠(yuǎn)不會(huì)調(diào)用");
});
NSLog(@"永遠(yuǎn)不會(huì) run");
-
2-2氓润、用戶強(qiáng)制退出
類似強(qiáng)制關(guān)機(jī)的情況赂乐。
-
2-3、內(nèi)存不夠
在我們App運(yùn)行的過(guò)程中咖气,系統(tǒng)內(nèi)存緊張時(shí)通常會(huì)先發(fā)警告挨措,同時(shí)把后臺(tái)掛起的程序終止掉,最終如果還是內(nèi)存不夠的話就會(huì)終止掉當(dāng)前前臺(tái)的進(jìn)程崩溪。
也提醒我們要及時(shí)的殺掉不用的內(nèi)存浅役,否則內(nèi)存占用越來(lái)越高,一旦超過(guò)系統(tǒng)限制就會(huì)被系統(tǒng)殺死伶唯,然后就Carsh 啦觉既。
-
2-4、自己產(chǎn)生的 bug
最常見(jiàn)的數(shù)組越界乳幸,或者其他五花八門的瞪讼,反正就是自己產(chǎn)生的問(wèn)題,像上述遇到的問(wèn)題粹断。符欠。。
來(lái)自 常見(jiàn)的Crash類型瓶埋。
此處提醒希柿,去看看 DZNEmptyDataSet 和 CHTCollectionViewWaterfallLayout 中的 issues, 是不是里面的問(wèn)題养筒,到它們的 github 上的 issues 轉(zhuǎn)了一大圈狡汉,也沒(méi)有類似的問(wèn)題...
三、盡量讓其重現(xiàn)
線上的崩潰率不低闽颇,但是為什么我們自己測(cè)試不出來(lái)啦盾戴,暫時(shí)還是只能去分析具體崩潰的位置, 在又一次認(rèn)真的看堆棧崩潰信息發(fā)現(xiàn), crash 在 objc_msgSend()兵多,而發(fā)生這種情況的原因可能是:
- 向一個(gè)已經(jīng)釋放的對(duì)象發(fā)送消息尖啡,野指針之類的
- 接收者的內(nèi)存錯(cuò)誤橄仆。
反正就是接收者的問(wèn)題,而我上面圖中的那就是 self.emptyDataSetSource
啦衅斩,此時(shí)在想難道是它被提前釋放掉了盆顾,但是下面這個(gè) view ,無(wú)論如何都是在是會(huì)返回的啊
- (UIView *)customViewForEmptyDataSet:(UIScrollView *)scrollView
而且對(duì)于 CollectionView 或者 viewModel 都是強(qiáng)引用啊,在生命周期內(nèi)不會(huì)自己釋放掉啊畏梆。
PS一個(gè)點(diǎn):編譯優(yōu)化會(huì)使調(diào)用堆棧中指向第二段的調(diào)用點(diǎn)(call site)可能并不是真正導(dǎo)致崩潰的調(diào)用您宪。
此時(shí)突然想到了我們的崩潰軌跡,有著大量的 KVO痕跡
1奠涌、1秒前 DiscoveryViewController viewDidAppear:(,)
2宪巨、1秒前 HomeViewController viewDidAppear:(,)
3、2秒前 NSKVONotifying_UICollectionView handlePan:(UIScrollViewPanGestureRecognizer,)
4溜畅、2秒前 NSKVONotifying_UICollectionView handlePan:(UIScrollViewPanGestureRecognizer,)
是否和 KVO 有關(guān)捏卓??慈格?然而此處是沒(méi)有用 KVO的啊怠晴,而且用了地方都是處理過(guò)的,此時(shí)我們只能先再了解下KVO一個(gè)點(diǎn):
-
NSKVONotifying_UICollectionView 的由來(lái)
當(dāng)某個(gè)類的實(shí)例對(duì)象的key第一次被觀察時(shí)浴捆,系統(tǒng)就會(huì)在運(yùn)行期動(dòng)態(tài)地創(chuàng)建該類的一個(gè)派生類NSKVONotifying_類名
蒜田,在這個(gè)派生類中重寫(xiě)該類中被觀察的屬性的 setter 方法。
@property(nonatomic, readonly) UIPanGestureRecognizer *panGestureRecognizer NS_AVAILABLE_IOS(5_0);
@property(nonatomic) CGPoint contentOffset;
在添加KVO觀察后选泻,我們?cè)?ObserveValueForKeyPath 打上斷點(diǎn)物邑,看一下Object 。
此時(shí)isa指針被系統(tǒng)動(dòng)態(tài)的指向了派生類NSKVONotifying_UICollectionView
注意:KVO的本質(zhì)就是監(jiān)聽(tīng)對(duì)象的屬性進(jìn)行賦值的時(shí)候有沒(méi)有調(diào)用
setter
方法滔金。
本頁(yè)面沒(méi)有用到色解,那只能再次猜測(cè):是不是其他頁(yè)面(有用到空頁(yè)面,瀑布流)返回過(guò)來(lái)的餐茵,并且用來(lái)了KVO 而沒(méi)提前釋放 —— 一般是沒(méi)有的科阎,臨走前就算沒(méi)有釋放,也不會(huì)調(diào)用dzn_canDisplay
方法的忿族。
所以锣笨,此處穩(wěn)妥一點(diǎn)讓Bug重現(xiàn)的方法就是 找到一個(gè)操作,會(huì)涉及方法
- (BOOL)dzn_canDisplay
,
- (CGSize)collectionViewContentSize
而且又歷經(jīng) HomeViewController
道批,DiscoveryViewController
错英,暫時(shí)符合該系列行為的就是:
- 啟動(dòng) app 的操作。
KVO 那塊可以理解是 contentOffset
的改變隆豹。接下來(lái)是重點(diǎn)測(cè)試這塊啦椭岩,但是一直還是無(wú)法重現(xiàn)該崩潰。
四、偽解決它
測(cè)試了一整天判哥,就沒(méi)有崩潰一次献雅,從來(lái)沒(méi)有想到過(guò)有一天居然想讓自己的項(xiàng)目崩掉。塌计。挺身。
回顧一下,我們之前版本 和 這一版本在啟動(dòng)中做的改變锌仅,然后我更懵啦章钾,最后覺(jué)的一種可能是 這邊 self.viewModel 被提前釋放掉了,但我這是強(qiáng)引用叭惹邸贱傀!。剿吻。窍箍。(項(xiàng)目中用的 是MVVM)
暫時(shí)的做法: 增加更多的防空處理串纺。丽旅。。
??纺棺!同時(shí)我們這個(gè)項(xiàng)目被暫停下來(lái)啦榄笙,暫時(shí)都不會(huì)重新發(fā)版本啦,更不知道去如何解決它啦祷蝌。茅撞。。
PS更新:再次看聽(tīng)云巨朦,這幾天這個(gè) Bug 居然不重現(xiàn)了米丘,讓我更懵啦,只是出現(xiàn)一個(gè)類似這個(gè)bug的糊啡,就是具體崩潰軌跡有點(diǎn)不同拄查,真的懵了......
PS: 最有可能的原因
[self.collectionView performBatchUpdates:^{
[self.collectionView insertItemsAtIndexPaths:indexPaths];
} completion:NULL];
根據(jù)崩潰信息,后來(lái)一朋友立馬想到是這個(gè)問(wèn)題棚蓄,就是UICollectionView插入 insertItemsAtIndexPaths
的時(shí)候必須用一個(gè)方法堕扶,用到這個(gè) performBatchUpdates 的方法。
- (void)performBatchUpdates:(void (^ __nullable)(void))updates completion:(void (^ __nullable)(BOOL finished))completion; // allows multiple insert/delete/reload/move calls to be animated simultaneously. Nestable.
allows multiple insert/delete/reload/move calls to be animated simultaneously. Nestable.
畢竟這個(gè)是蘋(píng)果推薦的稍算,之前沒(méi)有用,還是不對(duì)的役拴。雖說(shuō)無(wú)法證實(shí)糊探,無(wú)法重現(xiàn),但看后面那個(gè)注釋以及崩潰信息,感覺(jué)還是比較可靠的侧到。
五勃教、 總結(jié)
暫時(shí)這個(gè)問(wèn)題沒(méi)有解決它,沒(méi)法重現(xiàn)匠抗,但是還是先理一下這個(gè)過(guò)程的問(wèn)題和收獲故源。
- 解決 bug 的思路歷程,需要再優(yōu)化汞贸;
- 進(jìn)一步了解 Carsh 文件绳军,以及常見(jiàn)原因;
- 對(duì) KVO 的實(shí)現(xiàn)矢腻,有了新的認(rèn)識(shí)门驾。
同時(shí),如有朋友知道上述類似問(wèn)題的解決方案多柑,歡迎告之奶是。
PS: 個(gè)人再次遇到這個(gè)BUG,有了些新理解竣灌。