解決 iOS13 中 heightForRowAtIndexPath 返回負(fù)數(shù)導(dǎo)致的崩潰(Invalid row height)

崩潰提示: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)行解決。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容