Swift :: Encode and Decode a value type’s instance

임의의 Struct, Enum 타입을 인코딩하라.

임의의 값 타입을 인코딩하는 법을 찾아보자. (클래스의 경우에는 NSCoding이 있으니…) 먼저 인코딩이 필요한 함수를 생각해보자. NSData는 특정 포인터와 그 길이를 가지고 데이터를 만들 수 있다. NSData(bytes:UnsafePointer<T>, length:Int) 여기서 크기는 타입의 크기인데, C와 같이 Swift도 sizeof() 함수를 제공한다. (C에서는 연산자이다.)

Swift에서 inout T 타입과 UnsafePointer<T> 타입은 구분되므로 (T가 옵셔널인 경우에는 포인터로 간주될 수 있다.) 보조 함수인 withUnsafePointer<T>(_:_:) 함수를 사용하여 inout 으로 넘긴 T 타입을 포인터로 바꿀 수 있다.

따라서 인코드 함수는 다음과 같이

func encode<T>(var value:T) -> NSData {
    let data = withUnsafePointer(&value){
        (p:UnsafePointer<T>) -> NSData in
        return NSData(bytes:p, length:sizeof(T.Type))
    }
    return data
}

작성할 수 있다. 디코드하는 함수는? 이건 영락없이 포인터를 만들어서 메모리를 신규로 할당하고 여기에 바이트를 채워넣는 수 밖에 없어보인다. (물론 NSValue로 만들어서 내놓는 방법이 있는데, Swift는 아직 @encode를 쓸 수 없다.)

그래서 UnsafeMutablePointer<T> 타입을 이용해서 실제로 포인터를 다뤄보자.

UnsafeMutablePointer

이 타입은 T 타입에 대한 메모리 포인터를 나타내는 Swift 타입이다.

  • advanceBy(_:) : n 개 만큼 뒤쪽의 요소를 가리키는 포인터를 리턴한다. n은 음수를 써서 앞쪽 포인터를 가리키게 할 수 있다.
  • assignFrom(:count:), assignBackwardFrom(:count:) : memcpy와 비슷.
  • dealloc() : 할당한 메모리를 해제한다.
  • destroy() : 포인터가 가리키고 있는 위치의 객체를 파괴한다. 파괴된 메모리 영역을 다시 쓰이기 전에 반드시 초기화 되어야 한다.
  • distanceTo(_:) : 특정 포인터와의 거리(T포인터의 개수)를 구한다.
  • initialize(_:) : 주어진 값으로 해당 포인터를 초기화한다.
  • initializeFrom(_:): 주어진 집합의 값들로 메모리를 초기화한다.
  • initializeFrom(_:, count:):
  • move(): T타입 값을 읽고 그 영역을 파괴한다.
  • moveAssignFrom(_:,count:): 특정 소스의 값을 복사한다. (memmove와 비슷)
  • moveInitializeFrom(:,count:), moveInitializeBackwardFrom(:,count:) : 특정 소스로부터 값을 거꾸로 복사해온다.
  • predecessor() : 앞 위치
  • successor() : 뒤 위치
  • put(_:) : ?
  • (static) alloc(_:) : N바이트만큼 메모리를 할당한다.

디코드 함수

디코드 과정은 다음과 같다. NSData-getBytes:length: 메소드를 통해서 특정 길이만큼을 건네받은 포인터로 복사해주는 기능이 있다. 따라서 NSRange에 대한 뮤터블한 포인터를 만들고 그 값을 복사한다. 그런 다음, move() 메소드를 통해 포인터 내의 값을 얻으면 된다.

func decode<T>(data:NSData) -> T {
    var pointer = UnsafeMutablePointer<T>.alloc(sizeof(T.Type))
    data(getBytes:pointer, length:data.length)
    let result = pointer.move()
    pointer.dealloc()
    return result
}

이로써, Struct, Enum 등 Swift의 커스텀 타입에 대한 바이트 인코딩/디코딩을 할 수 있게 되었다.