Xcode 16 Beta自定义UICollectionViewCell出队错误排查求助
Xcode 16 Beta开发iOS应用时UICollectionView运行时异常排查与解决
问题现象
运行iOS应用时触发NSInternalInconsistencyException异常,错误核心提示如下:
*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Expected dequeued view to be returned to the collection view in preparation for display. When the collection view's data source is asked to provide a view for a given index path, ensure that a single view is dequeued and returned to the collection view. Avoid dequeuing views without a request from the collection view. For retrieving an existing view in the collection view, use -[UICollectionView cellForItemAtIndexPath:] or -[UICollectionView supplementaryViewForElementKind:atIndexPath:].
异常在@UIApplicationMain标记的入口处抛出,关联自定义单元格<MyProject.MyCollectionViewCell>。
完整堆栈跟踪:
*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Expected dequeued view to be returned to the collection view in preparation for display. When the collection view's data source is asked to provide a view for a given index path, ensure that a single view is dequeued and returned to the collection view. Avoid dequeuing views without a request from the collection view. For retrieving an existing view in the collection view, use -[UICollectionView cellForItemAtIndexPath:] or -[UICollectionView supplementaryViewForElementKind:atIndexPath:]. Dequeued view: <MyProject.MyCollectionViewCell: 0x15393d780; baseClass = UICollectionViewCell; frame = (34 35; 325 90); opaque = NO; backgroundColor = UIExtendedGrayColorSpace 0 0; layer = <CALayer: 0x6000045af9a0>>;; Collection view: <UICollectionView: 0x11ddf1e00; frame = (0 0; 393 582.333); clipsToBounds = YES; autoresize = RM+BM; gestureRecognizers = <NSArray: 0x600000de1920>; backgroundColor = <UIDynamicCatalogColor: 0x60000219acb0; name = sptBackground>; layer = <CALayer: 0x6000007510a0>; contentOffset: {0, 0}; contentSize: {393, 159}; adjustedContentInset: {0, 0, 0, 0}; layout: <UICollectionViewFlowLayout: 0x1042a4c70>; dataSource: <MyProject.MyCollectionsViewController: 0x11dde5200>>' *** First throw call stack: ( 0 CoreFoundation 0x00000001804b70ec __exceptionPreprocess + 172 1 libobjc.A.dylib 0x000000018008ede8 objc_exception_throw + 72 2 Foundation 0x0000000180e73aa8 _userInfoForFileAndLine + 0 3 UIKitCore 0x00000001851ace44 __43-[UICollectionView _updateVisibleCellsNow:]_block_invoke.444 + 136 4 UIKitCore 0x0000000185a9811c -[_UICollectionViewSubviewManager removeAllDequeuedViewsWithEnumerator:] + 188 5 UIKitCore 0x00000001851ac90c -[UICollectionView _updateVisibleCellsNow:] + 4000 6 UIKitCore 0x00000001851b1714 -[UICollectionView layoutSubviews] + 284 7 UIKitCore 0x0000000186016438 -[UIView(CALayerDelegate) layoutSublayersOfLayer:] + 2404 8 QuartzCore 0x000000018b059eb0 _ZN2CA5Layer16layout_if_neededEPNS_11TransactionE + 432 9 UIKitCore 0x000000018600583c -[UIView(Hierarchy) layoutBelowIfNeeded] + 308 10 UIKitCore 0x000000018529304c -[UINavigationController _layoutViewController:] + 812 11 UIKitCore 0x000000018528cac4 -[UINavigationController _layoutTopViewControllerLookForNested:] + 416 12 UIKitCore 0x000000018528b004 -[UINavigationController _didEndTransitionFromView:toView:wasCustom:] + 820 13 UIKitCore 0x0000000185c31908 -[_UIViewControllerTransitionConductor _startTransition:fromViewController:toViewController:] + 1004 14 UIKitCore 0x0000000185c311b0 -[_UIViewControllerTransitionConductor startDeferredTransitionIfNeeded] + 640 15 UIKitCore 0x0000000185293d4c -[UINavigationController __viewWillLayoutSubviews] + 80 16 UIKitCore 0x000000018527bb00 -[UILayoutContainerView layoutSubviews] + 168 17 UIKitCore 0x0000000186016438 -[UIView(CALayerDelegate) layoutSublayersOfLayer:] + 2404 18 QuartzCore 0x000000018b059eb0 _ZN2CA5Layer16layout_if_neededEPNS_11TransactionE + 432 19 QuartzCore 0x000000018b064c34 _ZN2CA5Layer28layout_and_display_if_neededEPNS_11TransactionE + 124 20 QuartzCore 0x000000018af99c58 _ZN2CA7Context18commit_transactionEPNS_11TransactionEdPd + 464 21 QuartzCore 0x000000018afc8468 _ZN2CA11Transaction6commitEv + 652 22 UIKitCore 0x0000000185ab8260 __34-[UIApplication _firstCommitBlock]_block_invoke_2 + 32 23 CoreFoundation 0x000000018041b0ec __CFRUNLOOP_IS_CALLING_OUT_TO_A_BLOCK__ + 20 24 CoreFoundation 0x000000018041a824 __CFRunLoopDoBlocks + 352 25 CoreFoundation 0x00000001804150c8 __CFRunLoopRun + 812 26 CoreFoundation 0x0000000180414960 CFRunLoopRunSpecific + 536 27 GraphicsServices 0x000000019016fb10 GSEventRunModal + 160 28 UIKitCore 0x0000000185a9f650 -[UIApplication _run] + 796 29 UIKitCore 0x0000000185aa3848 UIApplicationMain + 124 30 MyProject.debug.dylib 0x000000010983da34 __debug_main_executable_dylib_entry_point + 64 31 dyld 0x000000010298d410 start_sim + 20 32 ??? 0x0000000102b92154 0x0 + 4340654420 33 ??? 0x352f000000000000 0x0 + 3832281807915581440 ) libc++abi: terminating due to uncaught exception of type NSException
问题定位
异常根源在cellForItemAt方法的以下代码块:
let cell = collectionView.dequeueReusableCell(withReuseIdentifier:item.reuseIdentifier(), for: indexPath) as UICollectionViewCell switch cell { case is CollectionViewCell: if let customCell = cell as? CustomCollectionViewCell { if Device.isPhone && (item is FirstTypeItem || item is SecondTypeItem) { guard let phoneCell = collectionView.dequeueReusableCell(withReuseIdentifier: "PhoneCell", for: indexPath) as? PhoneCollectionCell else { return cell // Return the original cell if casting fails } phoneCell.setSomeValue(value) return phoneCell } customCell.setSomeValue(otherValue) return customCell } return cell
存在两个关键问题:
- 同一indexPath重复出队单元格:先调用一次
dequeueReusableCell获取cell,满足条件时又调用一次获取phoneCell,首次出队的cell未返回给集合视图,违反UICollectionView的复用机制,触发异常。 - 复用标识符拼写不一致:注册
PhoneCell时用的是"PHoneCell",但出队时用的是"PhoneCell",导致单元格加载失败,IBOutlet绑定为nil。
解决方案
1. 修正单元格出队逻辑
每个indexPath仅允许一次dequeueReusableCell调用,直接根据数据类型和设备类型选择对应的复用标识符,修改后的代码示例:
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { let item = dataSource[indexPath.item] // 直接根据条件选择要出队的单元格 if Device.isPhone && (item is FirstTypeItem || item is SecondTypeItem) { let phoneCell = collectionView.dequeueReusableCell(withReuseIdentifier: "PhoneCell", for: indexPath) as! PhoneCollectionCell phoneCell.setSomeValue(value) return phoneCell } else { let customCell = collectionView.dequeueReusableCell(withReuseIdentifier: item.reuseIdentifier(), for: indexPath) as! CustomCollectionViewCell customCell.setSomeValue(otherValue) return customCell } }
2. 修正复用标识符拼写错误
确保注册和使用的标识符完全一致:
// 注册PhoneCell时使用正确的标识符 collectionView.register(PhoneCollectionCell.self, forCellWithReuseIdentifier: "PhoneCell")
3. 额外检查(可选)
- 若使用xib创建
PhoneCollectionCell,需用xib方式注册:collectionView.register(UINib(nibName: "PhoneCollectionCell", bundle: nil), forCellWithReuseIdentifier: "PhoneCell") - 检查
PhoneCollectionCell的IBOutlet连接是否正确,避免因视图未加载导致的nil问题。
内容的提问来源于stack exchange,提问作者Martin Van Nostrand
相关产品推荐
相关产品推荐

