Day 48 - NSCoding

sun02·2021년 11월 4일
0

100 days of Swift 

목록 보기
33/40

UserDefaults에 정수, 날짜, 문자열, 배열 등을 저장할 수 있지만
몇 가지 규칙을 따르면 모든 종류의 데이터를 저장할 수도 있다.

NSKeyedArchiver의 archivedData() 메서드를 사용하면 object 그래프를 Data로 변환한 다음
다름 object인 것처럼 이것을 UserDefaults에 작성할 수 있다.

  • object 그래프 : 너의 object + 해당 object가 참조하는 모든 object + 그 object가 참고하는 object ...

규칙은 매우 간단하다

  1. 모든 데이터 타입은 다음중 하나여야 한다. : boolean, integer, float, double, string, array, dictionary, date, 또는 규칙2를 만족하는 class

  2. 만약 데이터 타입이 class라면, object 그래프를 보관하는데 사용되는 NSCoding 프로토콜을 따라야한다.

  3. 만약 데이터 타입이 array나 dictionary라면, 모든 키와 값은 규칙1 또는 규칙2를 만족해야한다.

많은 애플의 자체 class들이 NSCoding을 지원한다.
하지만 사용자가 만든 클래스는 적어도 default로 NSCoding을 따르지는 않는다.

따라서, 만든 Person 클래스를 다음과 같이 수정한다.


class Person: NSObject, **NSCoding** {

그런데 두가지 의문점이 있다

  • 구조체도 잘 작동하는데 여기서 클래스를 사용해야하는 이유가 뭘까?(실제로, 구조체는 기본 이니셜라이저도 제공되기 때문에 더 좋다)
  • 왜 NSObject를 상속받아야하는가?

보다시피, NSCoding으로 작업하려면 object를 사용해야하며 string, array, dictionary의 경우 개체와 상호교환이 가능한 구조체를 사용해야한다.
만약 Person 구조체를 struct로 만들었다면 NSCoding을 사용할 수 없다.

또한 NSObject를 상속받는 이유는 NSCoding을 사용하기 위해 필요하기 때문이다.

NSCoding 프로토콜을 따르게되면, encode()와 새로운 이니셜라이져 이 두 가지 메서드를 구현해야한다.

먼저 NSCoder라 불리는 새 클래스를 사용할 것인데 이것은 UserDefaults에서 함께 사용될 수 있도록 데이터 encoding(쓰기) 및 decoding(읽기) 를 모두 담당한다.

그 다음, 새로운 이니셜라이저는 required 키워드로 선언되어야한다. 이것은 "누군가가 이 클래스의 하위 클래스를 만들려고 한다면 이 메서드를 구현해야한다" 라는 의미이다.
또는 required의 대안으로 클래스의 하위 클래스를 만들 수 없다고 선언할 수도 있다. 이 경우에는 하위 클래스를 만들 수 없기 때문에 required가 필요하지 않다.


required init(coder aDecoder: NSCoder) {
	name = aDecoder.decodeObject(forKey: "name") as? String ?? ""
    image = aDecoder.decodeObject(forKey: "image") as? String ?? ""
}

func encode(with aCoder: NSCoder) {
	aCoder.encode(name, forkey: "name")
    aCoder.encode(image, forKey: "image")
}
  • 다음과 같이 두 가지 메서드(이니셜라이져, encode())를 추가해주었다.

이니셜라이져는 이 클래스의 object를 불러올 때 사용되고, encode()는 저장할 때 사용된다.
UserDefaults를 사용할 때와 코드는 매우 비슷하지만, as? 타입 캐스팅과 nil 병합 연산자를 추가해주었다.

이와 같은 변화로, Person 클래스는 이제 NSCoding을 따르기 때문에 우리는 people 배열을 불러오고 저장하는 코드를 작성할 수 있다.

UserDefaults에 Data object를 작성할 수 있지만 현재 Data object는 없고 그냥 배열만 갖고 있다.

다행히, NSKeyedArchiver의 archivedData()메서드가 앞에서 추가해 준 NSCoding의 메서드들을 사용하여 object 그래프를 Data object로 바꾸어준다.

사람들을 추가하거나, 이름을 바꾸는 등 배열에 변화를 야기하는 어디서나 사용할 수 있도록 save() 메서드를 만들자.


func save() {
	if let savedData = try? NSKeyedArchiver.archivedData(withRootObject: people, requiringSecureCoding: false) {
    	let defaults = UserDefaults.standard
        defaults.set(savedData, forKey: "people")
    }
}
  • 첫 번째 줄에서 배열(people)을 Data object로 변환해주고
  • 두 번째, 세 번째 줄에서 UserDafaults에 해당 data를 저장한다.

최종적으로, 앱이 실행될 때 디스크에서 배열을 로드해야하기 때문에, viewDidLoad()에 아래의 코드를 작성한다.


let defaults = UserDefaults.standard

if let savedPeople = defaults.object(forKey: "people") as? Data {
	if let decodedPeople = try? NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(savedPeople) as? [Person] {
    	people = decodedPeople
    }
}
  • object(forKey:)를 사용하여 optional Data를 가져오고, if let... as? 를 사용하여 언래핑한다.
  • NSKeyedUnarchiver의 unarchiveTopLevelObjectWithData 메서드를 사용해서 data를 object 그래프- Person object의 배열 - 로 변환한다.

0개의 댓글