본문 바로가기
Dev/Issue

[Error] object has been deleted or invalidated.

by Callie_ 2023. 11. 1.

 

문제가 있었던 코드

 

 

환경

  • iOS 16이상
  • Swift 5.9
  • Xcode 15
     

문제

개인 앱 프로젝트에서 DiffableDataSource로 CollectionView UI를 사용하고 있었다. Realm 데이터베이스에서 데이터 생성(create)은 문제없이 작동했지만, delete를 시도할 때마다 앱이 비정상 종료되는 오류가 발생했다. 특히 CollectionView의 스냅샷을 찍는 부분에서 'object has been deleted or invalidated'라는 오류 메시지가 나타났다.

 

이 오류는 '이미 삭제되었거나 유효하지 않은 객체에 접근하려 한다'는 의미였다. 객체가 하나일 때는 정상적으로 삭제되었지만, 두 개 이상일 때부터 삭제 시 위 오류가 발생하며 앱이 멈추는 상황이었다.


 

해결

처음에는 Realm과 관련된 문제라고 생각하여 Realm을 사용하는 모든 코드를 확인했다.

 

그러나,

  1. 삭제 시 앱이 멈추더라도 Realm 데이터베이스에서는 해당 셀과 데이터가 실제로 삭제되고 있었다.
  2. print 문을 통해 확인했을 때도 메서드 자체에는 문제가 없었다.

이러러 내가 예상했던 것과 달리 Realm의 문제는 아니었다.

 

오류 메시지가 DiffableDataSource 부분에서 발생하고 있었기 때문에, DiffableDataSource의 업데이트(apply) 메서드에 문제가 있다고 판단했다. DiffableDataSource를 사용할 때 reloadData의 역할을 스냅샷이 대신하는데, apply 메서드의 종류 선택이 문제의 원인이었다는 것을 일주일간의 고군분투 끝에 멘토님의 도움으로 알게 되었다. 오류가 발생한 지점도 정확히 셀의 개수를 삭제 후의 개수로 업데이트해주는 메서드로 이어질 때 앱이 비정상 종료되었기 때문에, apply 사용법이 문제라는 확신을 가졌다.

 

내가 프로젝트에서 DiffableDataSource를 사용한 주된 이유는 애니메이션 효과 때문이었다. 그런데 내가 사용하고 있던 apply 메서드(animatingDifferences: true 옵션)는 새로 업데이트되기 전 셀의 위치를 기억하고, 그 위치에서 업데이트를 시 시도했다. 따라서 객체를 삭제해버리면 스냅샷에는 여전히 해당 객체가 존재하는 것으로 인식하는데, 실제로는 이미 삭제된 상태였으므로 'object has been deleted or invalidated' 오류가 발생했던 것이다. 말 그대로 스냅샷이 이미 삭제된 객체에 접근하여 업데이트를 시도했기에 문제가 발생한 것이다.

 


수정한 코드

 
apply 메서드의 종류 중 applySnapshotUsingReloadData라는 것이 있었다. 기존에 사용하던 apply(snapshot, animatingDifferences: true) 부분을 applySnapshotUsingReloadData(snapshot)으로 수정하자, 접근 관련 오류가 해결되었다.

 

applySnapshotUsingReloadData에 대해 공식 문서를 확인해 본 결과, "현재 상태와 새로운 상태의 차이를 계산하거나 애니메이션 변경에 영향을 받지 않고 스냅샷의 데이터 상태를 UI에 반영한다"고 정의되어 있다. 이 메서드는 스냅샷이 이전에 기억하고 있던 객체에 계산을 위해 접근하려 하지 않고, 바로 업데이트되는 데이터를 UI에 반영해줄 수 있게 하는 방식이다.


 

DiffableDatasource + Snapshot + apply  핵심 정리

모두 애플 공식 문서 내용 기반으로 정리했다.

 

  • DiffableDataSource 
    • CollectionView에 필요한 데이터를 관리하고, 데이터 변화를 스스로 추적하여 관련 UI를 업데이트하는 객체이다. Hashable 프로토콜을 준수하는 값으로 데이터의 상태를 확인하여 변화를 추적한다.
    • 이를 통해 CollectionView나 TableView에서 흔히 발생하는 'out of index' 오류를 방지할 수 있다.
  • Snapshot:
    • CollectionView가 그리는 UI의 진실된 데이터 상태를 나타내는 역할을 한다.
    • 스냅샷의 데이터가 'Truth하지 않다'는 것은 해당 데이터가 더 이상 유효하지 않거나 비어있음을 의미한다. 
  • apply: apply 메서드는 두 가지 종류가 있다. 하나는 iOS 13에 도입되었고, 다른 하나는 iOS 15에 도입되었다.
    • iOS 13 버전 apply: animatingDifferences가 true일 때만 데이터 차이(diff)를 계산하여 업데이트하고, false일 때는 reloadData처럼 동작한다.
    • iOS 15 버전 apply: animatingDifferences 값과 상관없이 항상 데이터 차이(diff)만 계산하여 업데이트한다.
    • 애니메이션 설정을 고려한다면 iOS 15 버전을 사용하는 것이 더 적절할 수 있다.
  • applySnapshotUsingReloadData
    • 현재 상태와 새로운 상태의 차이(diff)를 계산하거나 애니메이션 변화에 영향을 받지 않고 스냅샷의 데이터 상태를 UI에 반영해주는 메서드다. reloadData와 유사하게 동작하며, 데이터를 즉시 반영하는 데 초점을 맞춘다.