반응형
Notice
Recent Posts
Recent Comments
Link
Today
Total
01-25 03:17
«   2025/01   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
Archives
관리 메뉴

iOS 개발 기록 블로그

Xcode 16 + iOS 18 UICollectionView 크래시 원인 및 개선 방안: NSInternalInconsistencyException 본문

iOS

Xcode 16 + iOS 18 UICollectionView 크래시 원인 및 개선 방안: NSInternalInconsistencyException

crazydeer 2025. 1. 12. 18:44
반응형

에러 로그

*** 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: 
  <Geniet.HomeDABannerCell: 0x14d7e2700; 
   baseClass = UICollectionViewCell; 
   frame = (14040 0; 390 195); 
   layer = <CALayer: 0x300dab8c0>>;

Collection view: 
  <UICollectionView: 0x14d62ce00; 
   frame = (0 50; 390 195); 
   clipsToBounds = YES; 
   gestureRecognizers = <NSArray: 0x3002e2eb0>; 
   backgroundColor = <UIDynamicSystemColor: 0x3019969c0; name = systemBackgroundColor>; 
   layer = <CALayer: 0x300de69e0>; 
   contentOffset: {13651, 0}; 
   contentSize: {30030, 195}; 
   adjustedContentInset: {0, 0, 0, 0}; 
   layout: <UICollectionViewFlowLayout: 0x14d63af80>; 
   dataSource: <Geniet.HomeHeaderView: 0x14d236700; 
                baseClass = UICollectionViewCell; 
                frame = (0 0; 390 344); 
                clipsToBounds = YES; 
                backgroundColor = UIExtendedGrayColorSpace 1 1; 
                layer = <CALayer: 0x300d26bc0>>>

 

원인 분석

발생 위치

아래 코드에 의해 크래시 발생

let destinationIndexPath = IndexPath(item: nextPage, section: 0)
if nextPage < self.daBannerIndex.count, self.daCollectionView.dataSource?.collectionView(self.daCollectionView, cellForItemAt: destinationIndexPath) != nil {
  self.daCollectionView.scrollToItem(at: destinationIndexPath, at: .right, animated: true)
}
  • ⚠️ self.daCollectionView.dataSource?.collectionView(self.daCollectionView, cellForItemAt: destinationIndexPath) != nil

 

위 코드 해석

  • dataSource의 cellForItemAt: 메서드를 직접 호출
  • UICollectionView가 셀을 dequeue하지 않았음에도 강제로 데이터 소스 메서드를 호출하는 비정상적인 동작

 

발생 이유

NSInternalInconsistencyException

UICollectionView는 특정 인덱스의 셀을 dequeue하도록 요청했지만, 해당 셀이 적절히 처리되지 않았다는 뜻

 

Expected dequeued view to be returned to the collection view in preparation for display

셀을 dequeue한 후, UICollectionView에 반환되지 않았음을 나타냄

 

도출

  • 직접 호출된 cellForItemAt: 메서드는 시스템이 관리하는 UICollectionView와 동기화되지 않습니다.
  • 시스템은 해당 셀이 표시될 준비가 되었는지 제대로 알지 못하며, 호출 과정에서 시스템 상태를 손상시킵니다.
  • 따라서 크래시가 발생

 

개선 방안

  • daCollectionView의 cellForItem(at:)를 사용하여 셀의 존재 여부를 확인
if nextPage < self.daBannerIndex.count {
  let destinationIndexPath = IndexPath(item: nextPage, section: 0)
    self.daCollectionView.scrollToItem(at: destinationIndexPath, at: .right, animated: true)
}

 

결론

  • collectionView(_:cellForItemAt:)는 직접 호출하면 안 되는 메서드
  • 크래시는 UICollectionView와 데이터 소스의 상태가 비정상적으로 동기화되었기 때문에 발생
  • cellForItemAt: 호출을 제거하고, 데이터 유효성만 검증하도록 수정

 

추가로 기존 버전에서 발생하지 않았던 원인은 아래와 같습니다.

 

시스템 내부 구현 변화

  • Xcode 16과 iOS 18 조합에서는 UICollectionView가 collectionView(_:cellForItemAt:) 메서드 호출 시 더 엄격한 동기화를 요구하도록 변경된 것으로 보입니다.

 

검증 로직 추가

  • iOS 18에서는 NSInternalInconsistencyException을 통해 명시적으로 오류를 발생시킴으로써, 잘못된 호출을 방지하려고 한 것으로 보입니다.

 

 

추가 개념

collectionView(_:cellForItemAt:) 메서드:

UICollectionView가 dequeue하려고 할 때 보여줄 Cell을 반환하기 위해 내부적으로 호출되는 메서드

 

 

 

반응형