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