iOS/UIKit

NSKeyedArchiver

Hamp 2024. 9. 11. 12:45
반응형

NSKeyedArchiver

 

오브젝트의 데이터를 key로 참조해서 기록하는 encoder다.

 

 

NSKeyedUnarchiver

key로 참조해서 오브젝트의 데이터를  복원하는 decoder다.


UserDefaults와 차이점

나는 지금까지 데이터를 저장할 때 UserDefaults를 이용했지만 이 아카이버의 역할은 무엇일까??

  NSKeyedArchiver UserDefaults
저장 방식 Key - Value
데이터 타입 매핑 여부 O
상위 하위 관계 매핑 여부 O X

 

공통점이 많은 인터페이스 가지만 큰 차이점이 존재한다.

바로 상속과 같은 복잡한 데이터의 관계를 같이 저장하는데 차이가 있다.

 

흠.. 상하 관계가 있다는 것은 상속을 쓴다는 것이고 NSKeyedArchiver는 클래스를 저장할 때 많이 쓰이는군.. 으로 예상된다.

 

한번 예시 코드와 함께 사용법을 익혀보자.

 


전제 조건

루트와 하위 모든 클래스 타입에서 NSCoding과 NSObject 을 채택, 상속해야한다. 

애플에서 NScoding을 직접 채택하는것보다 NSSecureCoding을 채택하는게 좋다고하니 그 방식으로 진행해보자.

 

위 두 내용에 대한 포스팅은 아래를 참고하자

NScoding와 관련된 포스팅

NSObject와 관련된 포스팅

 

 

1.  클래스 정의

a.encode(with:)

말그대로 어떤 key로 어떤 value로 저장할지 명시한다.

위 사진과 같이 타입별로 정말 많은 api들이 제공된다.

 

b.init?(coder:)

반대로 key를 통해 저장할 값을 decode하는 과정을 겪는다.

 

다음 예는 Canvas란 root를 두고 Circle과 Triangle을 갖고 있는 구조이다.

 

다음 과정을 통해 저장과 복원과정을 살펴보자

 

import Foundation

final class Triangle : NSObject, NSSecureCoding {
    static var supportsSecureCoding: Bool {
        return true
    }
    
    
    let width: Double
    let height: Double
    
    init(width: Double, height: Double){
        self.width = width
        self.height = height
        print(#file, #function)
    }
    
    func encode(with coder: NSCoder) {
        // encode(value:, forKey:) value 값을 해당 key로 인코딩
        coder.encode(width, forKey: "width")
        coder.encode(height, forKey: "height")
        print(#file, #function)
    }
    
    init?(coder: NSCoder) {
        // 디코딩
        width = coder.decodeDouble(forKey: "width")
        height = coder.decodeDouble(forKey: "height")
        print(#file, #function)
    }

}


====================================================================================

import Foundation

final class Circle : NSObject, NSSecureCoding {
    
    static var supportsSecureCoding: Bool {
        return true
    }
    
    let radius : Double
    
    init(radius: Double){
        self.radius = radius
        print(#file, #function)
    }
    
    func encode(with coder: NSCoder) {
        // encode(value:, forKey:) value 값을 해당 key로 인코딩
        coder.encode(radius, forKey: "radius")
        print(#file, #function)
    }
    
    init?(coder: NSCoder) {
        // 디코딩
        radius = coder.decodeDouble(forKey: "radius")
        print(#file, #function)
    }
    
}


====================================================================================

import Foundation

final class Canvas: NSObject, NSSecureCoding {
    static var supportsSecureCoding: Bool {
        return true
    }
    
    let circle: Circle
    let triangle: Triangle
    
    init(circle: Circle, triangle: Triangle, owner: String) {
        self.circle = circle
        self.triangle = triangle
        print(#file, #function)
    }
    
    required init?(coder: NSCoder) {
        print(#file, #function)
        guard let circle = coder.decodeObject(of: Circle.self, forKey: "circle"),
              let triangle = coder.decodeObject(of: Triangle.self, forKey: "triangle")
              else { assert(false) }
              self.circle = circle
              self.triangle = triangle
    }
    
    func encode(with coder: NSCoder) {
        print(#file, #function)
        coder.encode(circle, forKey: "circle")
        coder.encode(triangle, forKey: "triangle")
    }
    
}

 


2. archiver and unarchiver interface 이용

    func archvie(rootObject: Canvas) -> Data {
        do {
            let data = try NSKeyedArchiver.archivedData(withRootObject: rootObject, requiringSecureCoding: true)
            return data
        } catch {
            fatalError("Encoing Error")
        }
    }
    
    func unArchive(data: Data) -> Canvas {
        do {
            let unArchiver = try NSKeyedUnarchiver(forReadingFrom: data)
            unArchiver.requiresSecureCoding = true
            guard let object = unArchiver.decodeObject(of: Canvas.self , forKey: NSKeyedArchiveRootObjectKey) else { fatalError("Decode Error")}
            return object
        } catch {
            fatalError("Decoding Eror")
        }
    }

결과

let data = archvie(rootObject: canvas)
let object = unArchive(data: data)


print("Data: \(data)")
print("object: \(object)")

 

참고

 

NSKeyedArchiver | Apple Developer Documentation

An encoder that stores an object’s data to an archive referenced by keys.

developer.apple.com

 

 

NSKeyedUnarchiver | Apple Developer Documentation

A decoder that restores data from an archive referenced by keys.

developer.apple.com

 

반응형