开发者

NSMutableDictionary causing EXC_BAD_ACCESS

I'm trying to fix an EXC_BAD_ACCESS error that is being thrown when i access a NSMutableDictionary in tableView:cellForRowAtIndexPath:indexPath. Right now it works when I fill ridesDict with a method loadHistoryFromDBExtended like so:

self.ridesDict = [self loadHistoryFromDBExtended]; 

NSLog(@"rides dict %@", self.ridesDict);

However, I don't want to call [self loadHistoryFromDBExtended] for every cell being loaded since the dictionary won't change so I tried moving:

self.ridesDict = [self loadHistoryFromDBExtended]; 

to viewDidLoad and now I am getting the EXC_BAD_ACCESS error when i use:

NSLog(@"rides dict %@", self.ridesDict);

in tableView:cellForRowAtIndexPath:indexPath. From what i've read it seems that i've got some memory retain/release issues but i can't seem to figure it out. I've tried [self.ridesDict retain] in viewDidLoad after the loadHistoryFromDBExtended method was called but that didn't help. I'm very new to this so I'd appreciate any pointers as to where to go.

Edit: Here is the loadHistoryFromDBExtended method:

-(NSMutableDictionary *)loadHistoryFromDBExtended
{   
    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];

    FMDatabase* db = [FMDatabase databaseWithPath:[self getDBPath]];

    if (![db open]) 
    {
        NSLog(@"Could not open db.");
        [pool release];
    }

    //get users
    FMResultSet *rs = [db executeQuery:@"SELECT * FROM R order by date desc"]; //query provides result set

    //create result array
    NSMutableDictionary *myDictionary = [[NSMutableDictionary alloc] init];

    while ([rs next])
    {           
        NSMutableArray *usersDictArray = [NSMutableArray array];

        //look up names for id's
        [usersDictArray addObject:[rs stringForColumn:@"rNames"]];
        [usersDictArray addObject:[rs stringForColumn:@"dName"]];
        [usersDictArray addObject:[rs stringForColumn:@"date"]];
        [myDictionary setObject:usersDictArray forKey:[rs stringForColumn:@"rID"]];

        [usersDictArray release];
    }   

    //return usersArray;
    return myDictionary;

    [myDiction开发者_运维问答ary release];

    [pool drain];
}


I wrote this blog to help understand and debug EXC_BAD_ACCESS

http://loufranco.com/blog/files/Understanding-EXC_BAD_ACCESS.html

In order of easiness

  1. Run a Build and Analyze -- do you get a clean build? Look at what it's saying, but you can ignore leak problems for now -- look for problems of sending messages to released objects

  2. Run with NSZombiesEnabled -- this makes objects never deallocate, and then complain if a message is sent to an object with retainCount of 0.

  3. Enable Guard Malloc and then use the special GDB commands to inspect the integrity of the heap. The problem is that you need to step through and do this before you crash to find the real problem. It might crash somewhere else closer to your problem though


There are numerous issues with how that method is implemented. In the check for whether the db could be opened or not, you release the autorelease pool, but then continue on to the rest of the code. I assume you would probably want to return nil at that point. In the [rs next] section, you create an NSMutableArray with +array, which creates an autoreleased object. As such, you shouldn't call [usersDictArray release], as that would be over-releasing. (In each loop, the "temporary", autoreleased usersDictArray instances will be stored in the pool autorelease pool. When you call [pool drain], the autorelease pool will send all of those temporary instances a release message). Towards the end, you have return myDictionary;, which causes the next 2 lines to never be reached. As a result, the autorelease pool you created would never be released (popped).

This is probably how I would implement it:

-(NSMutableDictionary *)loadHistoryFromDBExtended {   
    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];

    FMDatabase* db = [FMDatabase databaseWithPath:[self getDBPath]];

    if (![db open]) 
    {
        NSLog(@"Could not open db.");
        [pool release];
        return nil; // I'm assuming you should return nil here
    }

    //get users; query provides result set
    FMResultSet *rs = [db executeQuery:@"SELECT * FROM R order by date desc"];

    //create result array
    NSMutableDictionary *myDictionary = [[NSMutableDictionary alloc] init];

    while ([rs next])
    {           
        NSMutableArray *usersDictArray = [NSMutableArray array];
        //look up names for id's
        [usersDictArray addObject:[rs stringForColumn:@"rNames"]];
        [usersDictArray addObject:[rs stringForColumn:@"dName"]];
        [usersDictArray addObject:[rs stringForColumn:@"date"]];
        [myDictionary setObject:usersDictArray forKey:[rs stringForColumn:@"rID"]];
        // [usersDictArray release];
    }   
    [pool drain];
    return [myDictionary autorelease];
}

(Note that how I implemented this assumes that the reasoning behind creating a local autorelease pool is for performance, and that there is a global NSAutoreleasePool in place to absorb the final [myDictionary autorelease] autoreleased object).


Try to call:

[self.ridesDict retain];

before using the dictionary(maybe at class init). Don't forget to call:

[self.ridesDict release];
self.ridesDict = nil;

at dealloc.

Also check if you declare property in the header correctly. It must be:

@property (nonatomic, retain) NSMutableArray *ridesDict;

And add @synthesize ridesDict; to your class implementation.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜