2


I have Objective-C class that has C array property.
And I want to serialize the property with 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

What should I write in ???.
Thank you.

EDIT:
Thank you for answers.
But Which should I use decodeArrayOfObjCType:count:at: or NSData?
If I don't know count of array, I cannot use decodeArrayOfObjCType:count:at: method?

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

or

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

Thank you.

2 Answers 2

5

If you don't know how many values are in the array at the time of decoding, then you might want to use NSData. So, you might have two properties, one for the intData, but another for the count of items in the array:

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

Or, if you didn't want to use NSData, you could use 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];
    }
}

The Encoding and Decoding C Data Types of the Archives and Serializations Programming Guide outlines some additional considerations:

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.

Sign up to request clarification or add additional context in comments.

9 Comments

Thank you for your answer. If I don't know count of array at decode time, then I cannot use decodeArrayOfObjCType:count:at: method? Please read my edited comment. Thank you.
I don't see how you can use decodeArrayOfObjCType unless you know the count in advance. That's why I suggested encodeObject approach. You can use encodeBytes, too.
@noprops just encode the count first, before encoding the items in the array. Then when you decode, you decode the count first and after that you know how many there are.
@AbhiBeckert But decodeArrayOfObjCType doesn't take a key. How do you encode multiple values when one of them doesn't take a key?
@Rob there are no keys if you use NSCoding directly. Keys only exist if you use NSKeyedArchiver which is not required. Note that decodeArrayOfObjCType is part of NSCoding and does not exist in NSKeyedArchiver. NSCoding will just give you the next value each time you decode something, in the same order as you encoded it.
|
4
-(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];
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.