Как сериализовать массив C с помощью NSCoding?



У меня есть Objective-C класс, который имеет свойство массива 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:кол-во:По: методу?

-(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];
    }
}

Спасибо.

2 2

2 ответа:

Если вы не знаете, сколько значений находится в массиве в момент декодирования, то вы можете использовать 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];
    }
}

Кодирование и декодирование типов данных C руководства по программированию архивов и Сериализаций описывает некоторые дополнительные соображения:

Массивы простых типов

Если вы кодируете массив байтов, вы можете просто использовать предоставленные методы для этого.

Для других арифметических типов создайте объект NSData с массивом. Обратите внимание, что в этом случае вы несете ответственность за решение проблем с конечностью платформы. Конечность платформы может быть обработана двумя общими способами. Первый способ заключается в преобразовании элементов массива (точнее, временной копии массива) в каноническую конечность, большую или малую, по одной за раз с функциями, обсуждаемыми в разделе замена байтов в универсальных руководствах по двоичному программированию, второе издание (см. также "порядок байтов" в Справочник по функциям фундамента) и дайте этот результат NSData в качестве буфера. (Или вы можете записать байты непосредственно с помощью encodeBytes:length:forKey:.) Во время декодирования вы должны повернуть процесс вспять, преобразовав большую или малую конечную каноническую форму в текущее представление хоста. Другой метод заключается в использовании массива как есть и записи в отдельном ключевом значении (возможно, логическом), которое было конечным значением хоста при создании архива. Во время декодирования считайте ключ endian и сравните его с endianness текущего хоста и замените значения только в том случае, если они отличаются.

Кроме того, вы можете архивировать каждый элемент массива отдельно как их собственный тип, возможно, используя имена ключей, вдохновленные синтаксисом массива, например “theArray[0]”, “theArray[1]”, и так далее. Это не ужасно эффективная техника, но вы можете игнорировать эндианские проблемы.

-(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];
}