NSValue NSData 변환
NSValue
를 NSData
로 변환하는 방법에 대해 생각해보자. 코어데이터 등에서 표준으로 지원하는 타입이 아닌 경우, NSData
를 이용해서 이진데이터로 저장하는 방법이 있는데, 예를 들어서 NSRange
(이건 C구조체이다.)를 이진데이터로 바꾸는 것은 다음과 같이 하면 된다.
NSRange r = NSMakeRange(0, 10);
NSValue* v = [NSValue valueWithRange:r];
NSData* d1 = [NSKeyedArchiver archivedDataWithRootObject:v];
구조체나 스칼라값 타입은 NSValue
로 감싼 다음 이진데이터로 직렬화할 수 있다. NSValue
역시 NSCoding
프로토콜을 따르므로 위와 같이 처리하면 되는데….
NSLog(@"%d", [d1 length]);
// 128
근데 변환한 크기가 무려 128 바이트나 된다. 고작 float타입의 멤버 두 개를 가진 구조체(16바이트)가 자리수가 2개나 차이나게 뻥튀기 된다.
NSUInteger size = 0;
NSUInteger tAlignment = 0;
const char* vType = v.objCType;
NSGetSizeAndAlignment(vType, &size, NULL);
NSLog(@"%d", size);
// 16
원래는 16바이트짜리 구조체였던 것이다. NSData는 최종적으로 연속된 메모리 영역을 래핑하는 객체이므로, 시작 주소와 크기를 알면 그 블럭을 감싼 데이터 객체를 만들 수 있다. 따라서 아래와 같이하여…
NSData *d2 = [NSData dataWithBytes:&r length:size];
NSLog(@"%d", [d2 length]);
// 16
이렇게 좀더 효율적으로 구조체를 이진데이터로 만들 수 있다. 이를 다시 구조체로 원복하는 과정은
void* ptr = malloc(sizeof(NSRange));
[data getByte:ptr length:[data.length]];
NSRange r = <em>((NSRange</em>)ptr);
이면 충분하다.
여기서 사용된 NSGetSizeAndAlignment()
함수를 이용하면 임의의 구조체나 타입에 대해서 (사실 sizeof()를 써도 되는 것 아닌지?) 인코딩 문자열을 통해 정확한 사이즈를 구할 수 있고, 이를 이용해서 NSData로 변환 가능하다.
문제는 Swift의 경우에는 @encode
디렉티브가 없다는 것과 C 포인터를 쉽게 다루기가 어렵다는 것인데… 이에 대해서는 별도로 포스팅하도록 하겠다.