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