一次从xib修改成纯代码带来的惨剧

今天在项目中遇到一个问题,是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 - 简书 

相关文章
相关标签/搜索