崩潰提示:Invalid row height (-XXX) provided by table delegate. Value must be at least 0.0, or UITableViewAutomaticDimension.
全局防護(hù):FixInvalidRowHeight 將 UITableView (FixInvalidRowHeight) 拖到項(xiàng)目中即可
字面意思就是說 rowHeight 不能為負(fù)數(shù),否則會導(dǎo)致內(nèi)部計(jì)算錯誤。
正常來說寫代碼時也不會說返回個高度為負(fù)數(shù)的 rowHeight,可能是如下這種代碼導(dǎo)致的
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
retrue tableView.height - 100;
}
假如在 tableView 進(jìn)行正確的 layout 前就調(diào)用了 heightForRowAtIndexPath 方法,此時 tableView.height 為 0 導(dǎo)致算出來的 rowHeight 為負(fù)數(shù)。
防護(hù)措施一:排查代碼的調(diào)用時機(jī)有沒問題,保證在 tableView 的高度沒值時不會觸發(fā) heightForRowAtIndexPath代理方法
防護(hù)措施二:上面排查完還不放心的話加個 Max 就好了
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
retrue MAX(tableView.height - 100, 0);
}
防護(hù)措施三:(最后防線)
因?yàn)樵?iOS13 上如果返回負(fù)數(shù)是會導(dǎo)致直接閃退的,就算你的是像上述代碼導(dǎo)致的負(fù)數(shù),在重新 layout 是可以拿到 tableView 的正確高度后返回一個正確且不小于 0 的 rowHeight 也沒用,因?yàn)?App 已經(jīng)閃退,因此就有了如下的防護(hù)措施。
通過崩潰堆棧可以分析出蘋果是在方法 _dataSourceHeightForRowAtIndexPath 或 _classicHeightForRowAtIndexPath 中拋出一個 Exception,因此 hook 此次的 exception 然后 return 即可,從而可以繼續(xù)后續(xù)的 layout 得到正確的 rowHeight 進(jìn)行布局。
FixOverrideImplementation(object_getClass(NSException.class), @selector(raise:format:arguments:), ^id(__unsafe_unretained Class originClass, SEL originCMD, IMP (^originalIMPProvider)(void)) {
return ^(NSException *selfObject, NSExceptionName raise, NSString *format, va_list argList) {
if (raise == NSInternalInconsistencyException &&
[format containsString:@"Invalid row height"] &&
[format containsString:@"provided by table delegate. Value must be at least 0.0, or UITableViewAutomaticDimension"]) {
NSLog(@"%@",format);
// TODO 這里將錯誤上報(bào)到自己的崩潰收集平臺(如 bugly)然后解決問題,忽略崩潰并不是本意,意在收集問題然后進(jìn)行解決
// [Bugly reportException:selfObject];
return;
}
void (*originSelectorIMP)(id, SEL, NSExceptionName name, NSString *, va_list);
originSelectorIMP = (void (*)(id, SEL, NSExceptionName name, NSString *, va_list))originalIMPProvider();
originSelectorIMP(selfObject, originCMD, raise, format, argList);
};
});
記得在崩潰處將 exception 上報(bào),畢竟忽略崩潰并不是本意,意在收集問題然后進(jìn)行解決。