1
votes

It appears that my initwithCoder and encodewithCoder is working, but I'm doing something stupid.

For simplicity, I'm showing the relevant code for my question. I have several objects that get NSData from a pointer and a length, so the values are being stored as a const char * and a length.

Here is the interface:

@interface IDImage : NSObject <NSCoding>    
@property (nonatomic) const char *templateData;
@property (nonatomic) NSUInteger templateSize;
- (void)encodeWithCoder:(NSCoder *)coder;
- (id)initWithCoder:(NSCoder *)coder;

Here is the implementation:

- (void)encodeWithCoder:(NSCoder *)encoder {
    [encoder encodeBytes:(const unsigned char*)self.templateData length:self.templateSize forKey:@"templateData"];
    [encoder encodeInteger:self.templateSize forKey:@"templateSize"];
}

- (id)initWithCoder:(NSCoder *)decoder {
    NSUInteger length = 0;
    self = [super init];
    if (self) {
        self.templateData = (const char *)[decoder decodeBytesForKey:@"templateData" returnedLength:&length];
        self.templateSize = [decoder decodeIntegerForKey:@"templateSize"];
    }
    return self;
}

I think I'm writing out the encoded bytes (assuming that the NSCoder is writing out first char all the way to the end, which is templateSize). But when I try to decode, I retrieve a pointer to templateData. How do I get the value? And if I have the value, will it only contain the first char? Meaning I have a char and a length but not the actual data that I meant to decode in bytes.

Can somebody please help me understand how to encode and decode data in bytes when my object contains only the first char and length?

1
Also, I stepped through my initWithCoder and encodeWithCoder and the values were the same (as expected) but in the calling code the values are different: templateToRead = [NSKeyedUnarchiver unarchiveObjectWithFile:[documentsDirectory stringByAppendingPathComponent:item]]; - Patricia

1 Answers

1
votes

If you look at the docs of the related method decodeBytesWithReturnedLength:, it says:

If you need the bytes beyond the scope of the current @autoreleasepool block, you must copy them.

I assume that decodeBytesForKey:returnedLength: follows the same rules. So your code should look like this:

- (id)initWithCoder:(NSCoder *)decoder 
{
  NSUInteger length = 0;
  self = [super init];
  if (self) 
  {
    void* temp = decoder decodeBytesForKey:@"templateData" returnedLength:&length];
    void *bytes = malloc(length);
    memcpy(bytes, temp, length);
    self.templateData = bytes;
    self.templateSize = [decoder decodeIntegerForKey:@"templateSize"];
  }
  return self;
}

Then add code like this to your dealloc:

- (void) dealloc;
{
  if (_templateData)
    free(_templateData);
)

Tip-o-the-hat to @nil, who found the (rather hidden) info in the docs about the temporary lifespan of the returned buffer.