今天在項目中遇到一個問題,是UICollectionView的一個DataSource方法- (UICollectionReusableView *)collectionView: viewForSupplementaryElementOfKind: atIndexPath:在同一個VC中被重複調用,第一次調用是正常的,第二次調用就會返回nil,致使crash,而這個方法是不容許返回crash的。c++
幾經定位,最後找到的緣由啼笑皆非,居然是忘記刪了VC的xib文件。
app
原由是項目中這個VC以前用了Xib,爲了使它更通用,以及之後使用JSPatch打補丁方便,就想把它改爲純代碼實現。其中的核心控件是一個自定義的UICollectionView。ide
我把IBOutlet關聯的collectionview屬性刪除了IBOutlet,這樣是刪除了關聯,並用純代碼建立了對應的collectionView。可是,最大的大意是沒有刪除VC對應的xib文件。ui
由於我以前在建立CustomViewController時,是勾選了Also Create XIB file的,那麼在用[[CustomViewController alloc]init]建立VC時,系統會自動在mainBundle中尋找同名的xib文件並加載。
spa
這就意味在在加載xib時建立了一個collectionView對象A,同時在個人純代碼中也建立了另外一個collectionView對象B,二者的DataSource和Delegate都指向了VC。而我在註冊header class時,是用self.collectionView註冊的,也就是說只有純代碼建立的collectionView才註冊了header class。由於collectionView對象A沒有註冊header class,那麼在使用- (UICollectionReusableView *)collectionView: viewForSupplementaryElementOfKind: atIndexPath:獲取對應的UICollectionReusableView對象時,用dequeueReusableSupplementaryViewOfKind: withReuseIdentifier: forIndexPath:方法獲得的返回值永遠爲nil,這就會致使crash。prototype
解決方法就是刪除VC對應的xib文件,就這麼簡單。調試
相關的錯誤日誌:日誌
*** Assertion failure in -[UICollectionView _createPreparedSupplementaryViewForElementOfKind:atIndexPath:withLayoutAttributes:applyAttributes:], /SourceCache/UIKit/UIKit-2903.2/UICollectionView.m:1401code
*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'the view returned from -collectionView:viewForSupplementaryElementOfKind:atIndexPath對象
(UICollectionElementKindSectionHeader,<NSIndexPath: 0x145f3f50> {length = 2, path = 0 - 0}) was not retrieved by calling -dequeueReusableSupplementaryViewOfKind:withReuseIdentifier:forIndexPath: or is nil'
(<UICollectionReusableView: 0x145f9400; frame = (0 0; 320 20); layer = <CALayer: 0x145f90c0>>)
*** Assertion failure in -[DFCollectionView _dequeueReusableViewOfKind:withIdentifier:forIndexPath:viewCategory:], /BuildRoot/Library/Caches/com.apple.xbs/Sources/UIKit/UIKit-3512.30.14/UICollectionView.m:3690
Exception: could not dequeue a view of kind: UICollectionElementKindSectionHeader with identifier CollectionEmptyDataHeader - must register a nib or a class for the identifier or connect a prototype cell in a storyboard
*** Assertion failure in -[DFCollectionView _createPreparedSupplementaryViewForElementOfKind:atIndexPath:withLayoutAttributes:applyAttributes:], /BuildRoot/Library/Caches/com.apple.xbs/Sources/UIKit/UIKit-3512.30.14/UICollectionView.m:1583
libc++abi.dylib: terminate_handler unexpectedly threw an exception
if(kind == UICollectionElementKindSectionHeader) { @try { theView = [theCollectionView dequeueReusableSupplementaryViewOfKind:UICollectionElementKindSectionHeader withReuseIdentifier:@"header" forIndexPath:theIndexPath]; } @catch (NSException * e) { NSLog(@"Exception: %@", e); theView = [[UICollectionReusableView alloc] init]; } @finally { return theView; } }
這份代碼雖然沒起做用,不建議使用,可是調試時能夠用其中的NSLog打印的信息來輔助定位問題。來源:uicollectionview - viewForSupplementaryElementOfKind is crashing on "index 0 beyond bounds for empty array" - Stack Overflow
這裏有一個相似的問題,但問題緣由徹底不同:
iOS UICollectionView 的坑:不能重複調用 dequeueReusableSupplementaryViewOfKind - 簡書