場景示意圖.png
場景是這樣的:
- APP首頁是一個
UITableView
列表,數(shù)據(jù)源設置為全局變量dataList
,點擊則跳轉第2頁詳情頁 - 在詳情頁中刷新
dataList
(元素個數(shù)未改變),并通知首頁reloadData
刷新UI - 然后刪除
dataList
一個元素,并通知首頁列表刷新reloadData
,導致程序崩潰并報
*** Terminating app due to uncaught exception 'NSRangeException', reason:
'*** -[__NSArrayM objectAtIndex:]: index 0 beyond bounds for empty array'
錯誤
此bug被測試提出后,筆者研究了半天,看似正常的刷新tableView
列表的邏輯怎么會出錯呢,最后發(fā)現(xiàn)原來是對UITableView
控價的運行機制沒有深刻了解,謹以此文分享給大家,以免重復掉坑。
1. UITableView的視圖加載邏輯
load 視圖
viewDidLoad
viewWillAppear:
加載完數(shù)據(jù)源之前 多次順序執(zhí)行以下方法:
numberOfSectionsInTableView:
numberOfRowsInSection:
視圖在屏幕上展示
viewDidAppear:
2. 已加載完數(shù)據(jù)源開始刷新
刷新
reloadData
多次執(zhí)行以下方法:
numberOfSectionsInTableView:
numberOfRowsInSection:
heightForRowAtIndexPath:
根據(jù)數(shù)據(jù)源元素數(shù)執(zhí)行相應的次數(shù)
cellForRowAtIndexPath:
因為復用池的緣故,分以下2種情況:
- 元素個數(shù)<頁面容納的行數(shù),執(zhí)行次數(shù)=元素個數(shù)
- 元素個數(shù)>頁面容納的行數(shù),執(zhí)行次數(shù)=頁面容納的行數(shù)
3. 在詳情頁刷新了數(shù)據(jù)源和列表后返回首頁
視圖將要展示在屏幕上
viewWillAppear:
關鍵點、坑點
cellForRowAtIndexPath:
注意:此處首頁-->詳情頁是在
Navi
棧里push
的:
- 若在詳情頁沒有對首頁
tableView
列表的數(shù)據(jù)源和列表進行刷新,則pop回首頁時,首頁列表不會自行刷新(即需reloadData
方法手動刷新) - 若在 詳情頁 已通過 協(xié)議 或 通知 回調對 首頁
tableView
列表的數(shù)據(jù)源和列表進行刷新,則pop回首頁時,首頁列表一開始會自行繞過numberOfRowsInSection:
而進入cellForRowAtIndexPath:
方法執(zhí)行,之后再根據(jù)通知reloaData
手動刷新。而此時首頁列表默認是按照push之前的數(shù)據(jù)源元素數(shù)執(zhí)行,此前詳情頁已刪除數(shù)據(jù)源中的一個元素,在此時數(shù)據(jù)源已改變,自然會產生數(shù)組越界問題
4. 解決問題
實際上只要了解了整個過程中 tableView
列表控件的一系列回調方法運行原理及邏輯,以上問題就迎刃而解了。
解決示例.png