开发者

Objective-C/iOS : Message sent to deallocated instance

This is my first post on stackoverflow.com so please be kind (rewind) ;)

I have a navigation based application whose purpose is to display blog posts (title) in a Table View (with JSON). The problem I ran into occurred when a cell got out of the screen and then back in. I was getting a EXC_BAD_ACCESS (because I sent a message to a deallocated instance), so I struggled to understand where it came from and I finally found a solution. But the fact is I don't exactly understand how the problem occurs. That's why I need someone to enlighten me, I think this is fundamental understanding !

When the connection to the JSON web service has finished loading, I parse the JSON code to obtain a list of blog posts (recentPosts), then I create a BlogArticle object for each post (blogArticle), store it in a MutableArray iVar (allEntries) and insert a row in the Table View :

- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
    [connection release];

    NSString *responseString = [[NSString alloc] initWithData:responseData encoding:NSUTF8StringEncoding];
    [responseData release];

    NSError *error;
    SBJsonParser *json = [[SBJsonParser new] autorelease];
    NSDictionary *recentPostsData = [json objectWithString:responseString error:&error];
    [responseString release];


    NSArray *recentPosts = [recentPostsData objectForKey:@"posts"];

    int i = 0;
    for (NSDictionary *post in recentPosts) {
        BlogArticle *blogArticle = [[BlogArticle alloc] initWithDictionary:post];
        [allEntries insertObject:blogArticle atIndex:i];
        [self.tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:[NSIndexPath indexPathForRow:i inSection:0]] withRowAnimation:UITableViewRowAnimationRight];
        i++;
   }
}

Here's the initialisation of the BlogArticle object which turned to be the origin of the problem :

- (id)initWithDict开发者_StackOverflow社区ionary:(NSDictionary *)article
{
    if (self = [super init])
    {
        // title = [[[article valueForKey:@"title"] gtm_stringByUnescapingFromHTML] copy];
        // title = [[NSString alloc] initWithString:[[article valueForKey:@"title"] gtm_stringByUnescapingFromHTML]];
        title = [[article valueForKey:@"title"] gtm_stringByUnescapingFromHTML];
    }

    return self;
}

So every Objective-C programmer who isn't as noobish as me is able to tell that title is never allocated before being assigned. If I uncomment one of the two lines above it will work. The program crashes exactly when I try to initialize a cell with that title variable, here :

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {

    static NSString *CellIdentifier = @"Cell";

    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    if (cell == nil) {
        cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
    }

    NSLog(@"indexPath.row = %i", indexPath.row);

    // Configure the cell.
    BlogArticle *article = [allEntries objectAtIndex:indexPath.row];

    cell.textLabel.text = article.title;

    return cell;
}

Now, what I need to understand is why it does compile/work without allocating the iVar and where exactly it causes trouble (or where exactly the content of title is released causing the program to crash). Any good resource (noob friendly) about memory management in iOS environment would be much appreciated.

Thanks in advance :)


This line

title = [[article valueForKey:@"title"] gtm_stringByUnescapingFromHTML];

is allocating an autoreleased string. Essentially, think of it that an autoreleased string will get released at the end of the method (though it can last longer, it's useful to think of it that way).

You know the string is autoreleased because the name of the method gtm_stringByUnescapingFromHTML does not start with alloc, new, copy or mutableCopy.

You can add retain to this to stop it getting autoreleased:

title = [[[article valueForKey:@"title"] gtm_stringByUnescapingFromHTML] retain];

Now you own the string, and it will not get released until you say so.

The best summary I know of is Apple's own documentation here.


Well, the problem is, that you have to initialize your object, if you want to manage the memory of it on your own. Why should you manage now the memory of title?

Quite simple: Every object reference, that is stored in an Array, Set, Dictionary etc. is managed by the Array, Dictionary and Set.

If you now just use this reference (by writing: "title = ...") in your cell, you will add the reference also to the cell. And now the cell is also responsible for the object-reference. So if the tableView wants to release your cells, which will happen from time to time to save memory, the cell will release your title-object. And this would cause the NSDitionary to be quite sad, since the NSDictionary wants to take care about the objects stored within itself.

So you could write the following in the tableView-method:

cell.textLabel.text = [article.title retain];

Or the commented lines of your own method. That means, you will "raise" the storage-level of your object up and if it gets released, the storage level itself will be decreased by one. If the storage-level will reach zero, it will be completely released (that should happen, if your tablecell is released AND your NSDIctionary)

I hope i could help you a bit :)

0

上一篇:

下一篇:

精彩评论

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

最新问答

问答排行榜