releasing NSKeyedUnarchiver causes crash

  • Hi all,

      once again I have a question I can't seem to figure out on my own.

    I'm trying to implementing an NSDocument application, more
    specifically the load/store part of it. It actually works as expected,
    reading and writing my object graph to and from a file works fine. The
    problem is that when releasing the NSKeyedUnarchiver instance used to
    read the data, the application crashes - and I don't know why.

    This is the exact code I'm using (tracks is an object that conforms to
    < NSCoding >)

    - (NSData *)dataOfType:(NSString *)typeName error:(NSError **)outError
    {
        NSMutableData *data = [[NSMutableData alloc] init];
        NSKeyedArchiver *archiver = [[NSKeyedArchiver alloc]
    initForWritingWithMutableData: data];
        [archiver setOutputFormat: NSPropertyListBinaryFormat_v1_0];
        [archiver encodeObject:tracks forKey: @"myobject"];
        [archiver finishEncoding];
        [archiver release];
        return [data autorelease];
    }

    - (BOOL)readFromData:(NSData *)data ofType:(NSString *)typeName error:
    (NSError **)outError
    {
        *outError = nil;
        NSKeyedUnarchiver *archiver;
        archiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:
    data];

        // release current data
        [tracks release];
        tracks = [archiver decodeObjectForKey: @"myobject"];
        [tracks retain];
        [archiver release]; // <--- crash here, no crash if commented out
        return YES;
    }

    The crash happens when [archiver release] is called. Since I own the
    object I have to release it and in my understanding it should no
    longer be necessary (the reading is finished at that point).
    Furthermore Leaks reports a leak if archiver is not released (which
    seem to be perfectly valid).

    I've also tried [archiver autorelease] but that only delays the crash
    a little.

    I don't understand the mistake I'm making, please help. Thanks!

    Regards
    Markus
    --
  • Am 18.05.2008 um 09:51 schrieb Markus Spoettl:

    > - (BOOL)readFromData:(NSData *)data ofType:(NSString *)typeName
    > error:(NSError **)outError
    > {
    > *outError = nil;
    > NSKeyedUnarchiver *archiver;
    > archiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:
    > data];
    >
    > // release current data
    > [tracks release];
    > tracks = [archiver decodeObjectForKey: @"myobject"];
    > [tracks retain];

    may be because this is missing:

    [archiver finishDecoding];

    > [archiver release]; // <--- crash here, no crash if commented out
    > return YES;
    > }

    Greetings
    Klaus
  • On May 18, 2008, at 5:25 AM, Klaus Backert wrote:
    >> - (BOOL)readFromData:(NSData *)data ofType:(NSString *)typeName
    >> error:(NSError **)outError
    >> {
    >> *outError = nil;
    >> NSKeyedUnarchiver *archiver;
    >> archiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:
    >> data];
    >>
    >> // release current data
    >> [tracks release];
    >> tracks = [archiver decodeObjectForKey: @"myobject"];
    >> [tracks retain];
    >
    > may be because this is missing:
    >
    > [archiver finishDecoding];

    That wasn't it but thanks for helping.

    The real reason was that I forgot to retain the objects decoded in
    tracks:initWithCoder: after decoding, so instead of

      timeStamp = [[decoder decodeObjectForKey:@"timeStamp"] retain];

    I had

      timeStamp = [decoder decodeObjectForKey:@"timeStamp"];

    and apparently the NSKeyedUnarchiver was the only think keeping that
    data alive.

    Well, I apologize for wasting your time.

    Regards
    Markus
    --
  • On 18 May 2008, at 18:04, Markus Spoettl wrote:

    > On May 18, 2008, at 5:25 AM, Klaus Backert wrote:
    >>> - (BOOL)readFromData:(NSData *)data ofType:(NSString *)typeName
    >>> error:(NSError **)outError
    >>> {
    >>> *outError = nil;
    >>> NSKeyedUnarchiver *archiver;
    >>> archiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:
    >>> data];
    >>>
    >>> // release current data
    >>> [tracks release];
    >>> tracks = [archiver decodeObjectForKey: @"myobject"];
    >>> [tracks retain];
    >>

    As tracks seems to be an ivar, it would likely be better practice to
    handle your memory management in an accessor, this way you never have
    to remember to write retain and release multiple times:

    - (void)setTracks:(id)newTracks;
    {
    if(tracks == newTracks)
      return;
    [tracks release];
    tracks = [newTracks retain];
    }

    you can the just call [self setTracks:newValue];

    see here:

    http://developer.apple.com/documentation/Cocoa/Conceptual/MemoryMgmt/Articl
    es/mmAccessorMethods.html


    To avoid writing accessors altogether, use Obj-C 2.0 properties.

    http://developer.apple.com/documentation/Cocoa/Conceptual/ObjectiveC/Articl
    es/chapter_5_section_2.html#/

    /apple_ref/doc/uid/TP30001163-CH17-SW13

    Sorry if I've told you things you know already and there was another
    perfectly valid reason for doing what you did.

    Jon
  • On May 18, 2008, at 1:53 PM, Jonathan Dann wrote:
    > Sorry if I've told you things you know already and there was another
    > perfectly valid reason for doing what you did.

    Actually it's a good question why I didn't use a property. I'm using
    properties all the time - one of the reasons being the automatic
    retain release handing. thanks for pointing that out.

    Regards
    Markus
    --
previous month may 2008 next month
MTWTFSS
      1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31  
Go to today