views:

73

answers:

1

I received a crash report from Apple and I'm trying to determine what happened. I haven't been able to recreate the crash by following their steps and I haven't seen a similar crash in any of my testing. Here is the important parts of the crash report:

9   libobjc.A.dylib                 0x00004838 objc_exception_throw + 64
10  CoreFoundation                  0x000a167c -[NSObject(NSObject) doesNotRecognizeSelector:] + 96
11  CoreFoundation                  0x000491d2 ___forwarding___ + 502
12  CoreFoundation                  0x00048f88 _CF_forwarding_prep_0 + 40
13  TheApp                          0x0001cd28 -[Tumblelog initWithDictionary:] (Tumblelog.m:40)
14  TheApp                          0x0001ef8c -[TumblrEngine userFromRequest:] (TumblrEngine.m:589)

This looks to me like my dictionary is being prematurely released. This code path is called multiple times during most runs of the app and it hasn't crashed for me yet, so I'm confident that I'm not accidentally sending the wrong object to initWithDictionary.

Here is the code from TumblrEngine and Tumblelog.

// TumblrEngine.m
- (TumblrUser *)userFromRequest:(ASIHTTPRequest *)request{
    NSData *data = [request responseData];
    NSError *parseError = nil;
    NSXMLDocument *doc = [[[NSXMLDocument alloc] initWithData:data options:NSXMLDocumentTidyXML error:&parseError] autorelease];
    NSDictionary *dictionary = [doc toDictionary];
    NSDictionary *userDict = [dictionary valueForKeyPath:kParseKeyPathUserInfo];
    TumblrUser *user = [[TumblrUser alloc] initWithDictionary:userDict];
    NSArray *tumblelogs = [dictionary valueForKeyPath:kParseKeyPathTumblelogsInfo];
    NSMutableArray *userTumblelogs = [NSMutableArray array];
    for(NSDictionary *tumblelogDictionary in tumblelogs){
        Tumblelog *tumblelog = [[Tumblelog alloc] initWithDictionary:tumblelogDictionary]; //line 589
        [userTumblelogs addObject:tumblelog];
        [tumblelog release];
    }
    [user setTumblelogs:userTumblelogs];
    return [user autorelease];

}

// Tumblelog.m
- (id)initWithDictionary:(NSDictionary *)aDictionary{
    if((self = [super init])){
        [self setAvatarURL:[aDictionary restURLForKey:kParseKeyTumblelogAvatarURL]]; //line 40
        // this was the line that started the crash
    }
    return self;
}

My main question is: do you see how it would be possible for aDictionary to be released at any point between when it's created and when I attempt to use it in Tumblelog.m?

Otherwise, I'm exploring if there's a problem loading the category on NSDictionary. It works fine when I directly load the app onto my three test phones (iPhone 4/iOS 4.1, iPhone 3GS/iOS 4.0.1, iPhone 3G/3.1.3). The phone that the app crashed on was iPhone 4/iOS 4.1, identical to my main test phone.

Only other thing I can think of is perhaps something in the binary I sent Apple was corrupted. I'm doubtful this is the answer, since those binaries are checksumed, but I'm running out of ideas here. I don't want to just resubmit if it's going to crash again on the tester's phone.

+1  A: 

It could be a threading issue (where the objects are being released on another thread), but that seems really unlikely with the code above.

It's far more likely (as @imaginaryboy says) that you don't really have a dictionary.

A much safer loop would look like this.

for(id tumblelogDictionary in tumblelogs){
    if ([tumblelogDictionary isKindOfClass:[NSDictionary class]]) {
        Tumblelog *tumblelog = [[Tumblelog alloc] initWithDictionary:tumblelogDictionary]; //line 589
        [userTumblelogs addObject:tumblelog];
        [tumblelog release];
    } else {
        // Appropriate error handling and / or logging.
    }
}

NOTE: I haven't actually tried compiling this. It may have a typo or syntax error or two.

Robot K
Thanks. In this situation, just checking to see if I've got a dictionary in the array would be acceptable. Unless anyone else has ideas, the only way I could reach this point without receiving an array of dictionaries is if I get mal-formed HTML from the server. If that's the case, leaving the `tumblelog` blank would be an acceptable outcome.
kubi