Unarchiving Objective-C objects

Moderator
Posts: 699
Joined: 2002.04
Post: #1
Long story short - I'm trying to learn the few bits of Objective-C/Cocoa I've (for years) neglected/never needed to use; amongst the bits: archiving and unarchiving objects.

Unfortunately, I've hit a bug I can't seem to fix: whilst I can archive and unarchive the objects, and send messages to the unarchived objects, any messages sent to the unarchived objects after the NSKeyedUnarchiver object used to unarchive the objects is sent the release message causes a crash due to EXC_BAD_ACCESS; I thought I understood the Objective-C memory management rules - it seems I was mistaken...

I'd appreciate it if anybody could take a look at my code and tell me where I'm going wrong...

(Edit: I've uploaded the source code here.)

The code for unarchiving the objects, with the lines that cause the crash marked:
Code:
TestObject *one, *two;
NSData *data = [[NSData alloc] initWithContentsOfFile:@"my_file"];
NSKeyedUnarchiver *unarchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:data];
one = [unarchiver decodeObjectForKey:@"object_one"];
[one retain];
two = [unarchiver decodeObjectForKey:@"object_two"];
[two retain];
[unarchiver finishDecoding];
[one print]; // No crash
[two print];
[unarchiver release];
[data release];
[one print]; // EXC_BAD_ACCESS; remove this line and the line below = no crash
[two print];

The code for archiving the objects:
Code:
TestObject *one = [[TestObject alloc] initWithString:@"one"];
TestObject *two = [[TestObject alloc] initWithString:@"two"];
NSMutableData *data = [[NSMutableData alloc] init];
NSKeyedArchiver *archiver = [[NSKeyedArchiver alloc] initForWritingWithMutableData:data];
[archiver encodeObject:one forKey:@"object_one"];
[archiver encodeObject:two forKey:@"object_two"];
[archiver finishEncoding];
if( [data writeToFile:@"my_file" atomically:YES] )
{
    NSLog( @"The objects were archived." );
}
else
{
    NSLog( @"The objects could not be archived." );
}
[archiver release];
[data release];
[one print];
[two print];

The code to the class:
Code:
static int g_int = 1;

@interface TestObject : NSObject <NSCoding>
{
    int m_int;
    NSString *m_string;
}

- (id)initWithString:(NSString*)p_string;
- (void)encodeWithCoder:(NSCoder*)p_coder;
- (id)initWithCoder:(NSCoder*)p_decoder;
- (void)print;
- (void)dealloc;

@end

@implementation TestObject

- (id)initWithString:(NSString*)p_string
{
    if( self = [super init] )
    {
        m_int = ( g_int++ );
        m_string = p_string;
        [m_string retain];
    }
    return self;
}

- (void)encodeWithCoder:(NSCoder*)p_coder
{
    [p_coder encodeInt:m_int forKey:@"theInt"];
    [p_coder encodeObject:m_string forKey:@"theString"];
}

- (id)initWithCoder:(NSCoder*)p_decoder
{
    if( self = [super init] )
    {
        m_int = [p_decoder decodeIntForKey:@"theInt"];
        m_string = [p_decoder decodeObjectForKey:@"theString"];
    }
    return self;
}

- (void)print
{
    NSLog( @"%d - %@", m_int, m_string );
}

- (void)dealloc
{
    [m_string release];
    [super dealloc];
}

@end

Mark Bishop
--
Student and freelance OS X & iOS developer
Quote this message in a reply
Moderator
Posts: 699
Joined: 2002.04
Post: #2
Okay, I'm an idiot: I just noticed I forgot to
Code:
[m_string retain];
after
Code:
m_string = [p_decoder decodeObjectForKey:@"theString"];
Blush

A pity I never noticed that omission during the hour I spent staring at the code before I started this thread Blush

Mark Bishop
--
Student and freelance OS X & iOS developer
Quote this message in a reply
Member
Posts: 353
Joined: 2002.04
Post: #3
Sometimes the most productive hour you can spend coding is to spend an hour not coding, away from the screen. Smile
Quote this message in a reply
Member
Posts: 96
Joined: 2011.07
Post: #4
(Sep 12, 2011 01:44 PM)sealfin Wrote:  Okay, I'm an idiot: I just noticed I forgot to
Code:
[m_string retain];
after
Code:
m_string = [p_decoder decodeObjectForKey:@"theString"];
Blush

A pity I never noticed that omission during the hour I spent staring at the code before I started this thread Blush

This is easy to forget. The unarchiver returns an object that has a retain count of zero.

Sometimes you get away with it if you assign to a property with the (retain) attribute (i.e., @property (retain) ... *myObject; and you call self.myObject=[p_decoder decode....]; when Objective-C automatically retains it for you. At times you forget that "self." and wonder why it's crashing.

respect,
pk

iFrog is coming.
Quote this message in a reply
Sage
Posts: 1,482
Joined: 2002.09
Post: #5
(Sep 12, 2011 05:39 PM)monteboyd Wrote:  Sometimes the most productive hour you can spend coding is to spend an hour not coding, away from the screen. Smile

Amen to that. It's happened to me a number of times where I will spend hours looking for a bug only to find out and finally give up. Then shortly after while not at my computer (and often far away from it) the solution will just pop right into my head.

Scott Lembcke - Howling Moon Software
Author of Chipmunk Physics - A fast and simple rigid body physics library in C.
Quote this message in a reply
Member
Posts: 104
Joined: 2002.04
Post: #6
The static analyzer in XCode 4 should have found that for you. Hitting Command-Shift-B on my project always makes me go "DOH!" for some error like that.
Quote this message in a reply
Post Reply