如何使用 NSCoding 序列化 C 数组?

How to serialize C array with NSCoding?


我有 Objective-C class 有 C 数组 属性.
我想用 NSCoding.

序列化 属性
@interface TestClass : NSObject <NSCoding>
@property (nonatomic) int* intArray;
@end

@implementation TestClass
-(instancetype)init
{
    self = [super init];
    if (self) {
        _intArray = (int *)malloc(sizeof(int) * 5);
        for (int i = 0; i < 5; i++) {
            _intArray[i] = 0;
        }
    }
    return self;
}
-(void)dealloc
{
    free(_intArray);
}
-(instancetype)initWithCoder:(NSCoder *)coder
{
    self = [super init];
    if (self) {
        //???
    }
    return self;
}
-(void)encodeWithCoder:(NSCoder *)coder
{
    //???
}
@end

我应该写什么???.
谢谢。

编辑:
谢谢你的回答。
但是我应该使用 decodeArrayOfObjCType:count:at: 还是 NSData?
如果我不知道数组的数量,我就不能使用 decodeArrayOfObjCType:count:at: 方法?

-(instancetype)initWithCoder:(NSCoder *)coder
{
    self = [super init];
    if (self) {
        _itemCount = [coder decodeIntForKey:kItemCount];
        _intArray = (int *)malloc(sizeof(int) * _itemCount);
        [coder decodeArrayOfObjCType:@encode(int) count:_itemCount at:_intArray];
    }
    return self;
}
-(void)encodeWithCoder:(NSCoder *)coder
{
    [coder encodeInt:_itemCount forKey:kItemCount];
    [coder encodeArrayOfObjCType:@encode(int) count:_itemCount at:_intArray];
}

-(instancetype)initWithCoder:(NSCoder *)coder
{
    self = [super init];
    if (self) {
        _itemCount = [coder decodeIntForKey:kItemCount];
        NSData *data = [coder decodeObjectForKey:kIntArray];
        if (data) {
            NSUInteger length = [data length];
            _intArray = (int *)malloc(length);
            [data getBytes:_intArray length:length];
        }
    }
    return self;
}

-(void)encodeWithCoder:(NSCoder *)coder
{
    [coder encodeInt:_itemCount forKey:kItemCount];
    if (_intArray) {
        NSData *data = [NSData dataWithBytes:_intArray length:sizeof(int) * _itemCount];
        [coder encodeObject:data forKey:kIntArray];
    }
}

谢谢。

-(instancetype)initWithCoder:(NSCoder *)coder
{
    self = [super init];
    if (self) {
        _intArray = (int *)malloc(sizeof(int) * 5);
        [coder decodeArrayOfObjCType:@encode(int) count:5 at:_intArray];
    }
    return self;
}

-(void)encodeWithCoder:(NSCoder *)coder {
    [coder encodeArrayOfObjCType:@encode(int) count:5 at:_intArray];
}

如果你不知道解码时数组中有多少个值,那么你可能想使用NSData。因此,您可能有两个属性,一个用于 intData,另一个用于数组中的 count 个项目:

- (instancetype)initWithCoder:(NSCoder *)coder
{
    self = [super init];
    if (self) {
        NSData *data = [coder decodeObjectForKey:kIntArrayKey];
        if (data) {
            NSUInteger length = [data length];
            self.count = length / sizeof(int);
            self.intArray = (int *)malloc(length);
            [data getBytes:_intArray length:length];
        }
    }
    return self;
}

- (void)encodeWithCoder:(NSCoder *)coder
{
    if (_intArray) {
        NSData *data = [NSData dataWithBytesNoCopy:self.intArray length:sizeof(int) * self.count freeWhenDone:NO];
        [coder encodeObject:data forKey:kIntArrayKey];
    }
}

或者,如果您不想使用 NSData,您可以使用 encodeBytes:length:forKey::

- (instancetype)initWithCoder:(NSCoder *)coder
{
    self = [super init];
    if (self) {
        NSUInteger length;
        const uint8_t *bytes = [coder decodeBytesForKey:kIntArrayKey returnedLength:&length];
        if (bytes) {
            self.count = length / sizeof(int);
            self.intArray = (int *)malloc(length);
            memcpy(_intArray, bytes, length);
        }
    }
    return self;
}

- (void)encodeWithCoder:(NSCoder *)coder
{
    if (_intArray) {
        [coder encodeBytes:(void *)self.intArray length:sizeof(int) * self.count forKey:kIntArrayKey];
    }
}

存档和序列化编程指南Encoding and Decoding C Data Types概述了一些额外的注意事项:

Arrays of Simple Types

If you are encoding an array of bytes, you can just use the provided methods to do so.

For other arithmetic types, create an NSData object with the array. Note that in this case, dealing with platform endianness issues is your responsibility. Platform endianness can be handled in two general ways. The first technique is to convert the elements of the array (or rather, a temporary copy of the array) into a canonical endianness, either big or little, one at a time with the functions discussed in Swapping Bytes in Universal Binary Programming Guidelines, Second Edition (see also “Byte Ordering” in Foundation Functions Reference) and give that result to the NSData as the buffer. (Or, you can write the bytes directly with encodeBytes:length:forKey:.) At decode time, you have to reverse the process, converting from the big or little endian canonical form to the current host representation. The other technique is to use the array as-is and record in a separate keyed value (perhaps a boolean) which endianness the host was when the archive was created. During decoding, read the endian key and compare it to the endianness of the current host and swap the values only if different.

Alternatively, you can archive each array element separately as their native type, perhaps by using key names inspired by the array syntax, like “theArray[0]”, “theArray[1]”, and so on. This is not a terribly efficient technique, but you can ignore endian issues.