안녕하세요.
이번에는 Core Data를 Optional로 설정했을 시 주의사항에 대해서 알아보겠습니다.
이번 글은 아래 글을 토대로 작성하였습니다.
- https://atomicbird.com/blog/clash-of-the-optionals/
## 1. 현상
Core Data에 Attribute를 추가하면 아래와 같은 설정 화면이 나옵니다.
(임의로 timestamp라는 attribute를 추가해봤어요.)
설정화면을 보면 기본적으로 Optional이 체크되어 있어요.
만약 timestamp가 Optional value가 아닌, 즉 항상 non-nil이 보장된다면 Optional 체크박스를 해제해주면 됩니다.
그런데 Xcode > Editor > Create NSManagedObject Subclass를 눌러서 코드로 뽑아보면 timestamp가 여전히 Optional 한 것을 볼 수 있어요..
흐으음.... 🤔 🤔 🤔
전 분명 Optional 체크박스를 해제했는데, 코드화 시키니 여전히 Optional 하다고 되어있네요.....????
(이게 오늘의 핵심 주제입니다.)
왜 그럴까요????
## 2. 이유
그 이유를 알기 위해서는, 우선 Swift와 Core Data를 분리해서 봐야 합니다.
Swift의 not optional의 의미 | Core Data의 not optional의 의미 |
초기화 시점 이후에는 property가 non-nil 이어야 한다. | Core Data에 저장되어 있는 값을 변경할 때, 변경할 값이 non-nil 이어야 한다. |
즉, Swift의 not optional과 Core Data의 not optional의 의미가 서로 다르기 때문에
우리의 똑똑한 Xcode는 코드로 추출했을 때 가장 안전한 방법인 Optional로 만들어주는 것이죠ㅠㅠ
(not optional로 했다가 런타임 때 크래쉬가 발생할 수 있기 때문이죠....ㅠㅠ)
이쯤에서 의문점이 하나 생길 것 같아요.
그냥 아래처럼 강제로 not optional로 바꿔주면 안 됨???
extension Event {
@NSManaged public var timestamp: Date // '?' 제거하면 not optional이 됨
}
결론은 가능하다!! 입니다. 하지만 전제조건이 있어요. 크래쉬가 나지 않게 잘~ 써야 합니다...;;;
timestamp 프로퍼티 앞에 @NSManaged 속성이 붙어있죠??
크래쉬가 나지 않게 어떻게 써야 하는가... 에 대해 알아보기 전에 우선 @NSManaged 속성에 대해 간단하게 알아봐야 할 것 같아요.
@NSManaged 속성을 사용하면 Swift Optional 규칙을 따르지 않게 됩니다.
즉, 프로퍼티가 not optional 일 경우, 초기화 시점이 아닌 값을 변경할 때 non-nil 이기만 하면 문제가 없다는 것이죠.
그래서 만약에 아래처럼 강제로 not optional로 바꿔줄 경우에 어떤 문제가 발생할 수 있냐면,
extension Event {
@NSManaged public var timestamp: Date
}
let newEvent = Event() // timestamp 값 설정하지 않았는데 컴파일 에러가 발생하지 않음.
print("Date : \(newEvent.timestamp)") // timestamp는 실질적으로 nil이기 때문에 크래쉬 발생
위 코드처럼, newEvent 인스턴스를 생성할 때 timestamp 값을 설정하지 않았지만 컴파일 에러가 발생하지 않습니다.
컴파일 에러가 발생하지 않으니, 런타임 때 newEvent.timestamp 프로퍼티로 접근하게 되면 nil 이기 때문에 크래쉬가 발생하게 되는 것이죠.
(속성을 not optional로 바꿈으로써, non-nil 제약조건이 초기화 시점 -> 값 변경 시점으로 변경되었기 때문이죠)
이런 문제는 쉽게 눈에 띄지 않기 때문에 앱 구동에 문제를 일으킬 가능성이 매우 높습니다ㅠㅠㅠ
## 3. 해결방법
프로퍼티의 속성을 not optional로 바꾸고 싶을 경우 발생할 수 있는 문제들에 대한 해결방법에 대해 소개하겠습니다.
### 3.1 (방법 1) not optional 프로퍼티에 접근할 때 nil 체크를 한다.
### 3.2 (방법 2) Default 값을 설정해준다.
#### 3.3 (방법 3) awakeFromInsert method를 구현한다. (값을 동적으로 할당해야 하는 경우)
이번 글은 여기서 마무리.
'Swift' 카테고리의 다른 글
CryptoKit을 사용한 암호화 (0) | 2022.02.23 |
---|---|
async/await 용 public API를 추가할 때 고려사항 (1) | 2022.02.23 |
enum 남용 주의 (0) | 2022.02.17 |
Literal 이란? (1) | 2022.02.07 |
Swift 인스턴스 method 목록 가져오는 방법 (0) | 2022.02.06 |