Search code examples
iphoneuitableviewios4scroll

Strange crash (EXC_BAD_ACCESS) scrolling UITableView reentering app after background


I'm using an UITableView with standard cell with subtitle and UIImageView. The scrolling is ok till I exit from the app. Then it goes background (I make nothing on delegate backgroun methods) and when I rerun the app, on the same view with the uitable, the scroll it's ok for some rows then the app crash in the method:

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

the code of the method is:

static NSString *CellIdentifier = @"Cell";

UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];

if (cell == nil) {

    cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier] autorelease];

  ALog(@"TRACE");

    }

  // Configure the cell...

 cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;

 NSDictionary *cellContent = (NSDictionary *)[self.items objectAtIndex:indexPath.row];

 Restaurant * r = (Restaurant *)[cellContent valueForKey:@"ristorante"];

 cell.textLabel.textColor = [UIColor colorWithRed:245.0 green:245.0 blue:245.0 alpha:0.8];

 cell.textLabel.text = r.nome;

 cell.detailTextLabel.textColor = [UIColor lightTextColor];

 cell.detailTextLabel.text = [r.indirizzo convertToString];

 UIImage *img = r.tipo.image; //[UIImage imageNamed:@"loghetto_pne.png"];

 cell.imageView.image = img; 

  //cell.imageView.clipsToBounds = YES;

  //cell.imageView.contentMode = UIViewContentModeScaleAspectFit;

 float sw= 48/img.size.width;

 float sh= 48/img.size.height;

 cell.imageView.transform = CGAffineTransformMakeScale(sw,sh);

  //[img release];



    return cell;

The crash is in the line:

cell.imageView.image = img;

From the stack trace I see that the execution goes on some internal framework code and then crash. The exception is not always the same (often is CATransaction count --> object di not respond to selector etc)

The code for Restaurant and Tipologia:

#import <Foundation/Foundation.h>
@class Zona;
@class Indirizzo;
@class Tipologia;

@interface Restaurant : NSObject {

@private
    NSUInteger idx;
    NSString *nome;
    NSString *telefono;
    Indirizzo *indirizzo;
    Zona *zona;
    Tipologia *tipo;

}

-(id)initWithIdx:(NSUInteger)index name:(NSString *)ristoName tel:(NSString *)ristoTel address:(Indirizzo *)ristoAdd zone:(Zona *)ristoZone;

@property (nonatomic) NSUInteger idx;
@property (nonatomic,retain) NSString *nome;
@property (nonatomic,retain) NSString *telefono;
@property (nonatomic,retain) Indirizzo *indirizzo;
@property (nonatomic,retain) Zona *zona;
@property (nonatomic,retain) Tipologia *tipo;


@end

Restaurant implementation:

#import "Restaurant.h"
#import "Zona.h"
#import "Indirizzo.h"
#import "Macro.h"


@implementation Restaurant

@synthesize idx, nome, indirizzo, telefono, zona, tipo;

-(id)initWithIdx:(NSUInteger)index name:(NSString *)ristoName tel:(NSString *)ristoTel address:(Indirizzo *)ristoAdd zone:(Zona *)ristoZone {
    [self init];
    bool error = NO;
    if (self) {
        idx = index;
        nome = [ristoName retain];
        telefono = [ristoTel retain];
        indirizzo = [ristoAdd retain];
        zona = [ristoZone retain];
    }

    return (error) ? nil : self;
}

- (id)init {
    self = [super init];
    if (self) {
        idx = 0;
        nome = @"";
        telefono = @"";
        indirizzo = nil;
        zona = nil;
        tipo = nil;
    }
    return self;
}

- (void)dealloc {
    [nome release];
    [indirizzo release];
    [telefono release];
    [zona release];
    ALog(@"TRACE");
    [tipo release];
    ALog(@"TRACE");
    [super dealloc];
}

@end

Tipologia interface and implementation:

#import <Foundation/Foundation.h>

typedef enum {
    kTipoRestUnknown = 0,
    kTipoRestRestaurant,
    kTipoRestBrunch,
    kTipoRestPizza,
    kTipoRestRegional,
    kTipoRestEthnic
} TipoRest;

@class ImageTest;

@interface Tipologia : NSObject {

@private    
    NSInteger idx;
    NSString *desc;
    UIImage *image;
    TipoRest type;
}

-(id)initWithIndex:(NSInteger) index description:(NSString *)descr ofType:(TipoRest) type;

@property (nonatomic) NSInteger idx;
@property (nonatomic) TipoRest type;
@property (nonatomic,retain) NSString *desc;
@property (nonatomic,retain) UIImage *image;


@end

#import "Tipologia.h"
#import "Macro.h"

@implementation Tipologia

@synthesize desc, idx, image, type;

#pragma mark -
#pragma mark Memory Management

-(id)initWithIndex:(NSInteger) index description:(NSString *)descr ofType:(TipoRest) type {
    self = [super init];
    if (self != nil) {
        self.idx = index;
        self.desc = descr;
        self.image = [UIImage imageNamed:@"immagineNA.png"];;
        self.type = type;
    }
    return self;
}

-(void)dealloc {
    [desc release];
    desc = nil;
    ALog(@"TRACE");
    [image release];
    image = nil;
    ALog(@"TRACE");
    [super dealloc];    
}

-(void)release {
    ALog(@"tipo.idx: %i, tipo.count: %i, tipo.imag: %@, tipo.img.count: %i", idx, [self retainCount], image, [image retainCount]);
    [super release];
}

EDIT 2 Some other code. The snippet where I initialize the images based on the type

if (sqlite3_prepare_v2(db, queryStrTipo, -1, &query, NULL) == SQLITE_OK) {
        restImage = [UIImage imageNamed:@"rest.png"];
        pizzaImage = [UIImage imageNamed:@"pizza.png"];
        etnImage = [UIImage imageNamed:@"etnico.png"];
        brunchImage = [UIImage imageNamed:@"wineglass-blue.png"];

        while(sqlite3_step(query) == SQLITE_ROW) { 
            NSString *desc = [NSString stringWithUTF8String:(char *)sqlite3_column_text(query, 1)];
            tipo = [[Tipologia alloc] initWithIndex:sqlite3_column_int(query, 0)
                                                description:desc ofType:kTipoRestUnknown];
            ALog(@"tipo.idx: %i, retain count: %i",tipo.idx, [tipo retainCount]);
            if ([desc compare:kTipoDescRestaurant options:NSCaseInsensitiveSearch] == NSOrderedSame) {
                tipo.type = kTipoRestRestaurant;
                tipo.image = restImage;
            } else 
            if ([desc compare:kTipoDescPizza options:NSCaseInsensitiveSearch] == NSOrderedSame) {
                tipo.type = kTipoRestPizza;
                tipo.image = pizzaImage;
            } else
            if ([desc compare:kTipoDescEtnico options:NSCaseInsensitiveSearch] == NSOrderedSame) {
                tipo.type = kTipoRestEthnic;
                tipo.image = etnImage;
            } else
            if ([desc compare:kTipoDescBrunch options:NSCaseInsensitiveSearch] == NSOrderedSame) {
                tipo.type = kTipoRestBrunch;
                tipo.image = brunchImage;
            } else
            if ([desc compare:kTipoDescRegionale options:NSCaseInsensitiveSearch] == NSOrderedSame) {
                tipo.type = kTipoRestRegional;
            }


            dictionary = [[NSMutableDictionary alloc] initWithObjectsAndKeys:[NSNumber numberWithInt:tipo.idx], @"index", tipo.desc, @"desc", nil];
            [listaTipologie addObject:tipo];
            [listaTemp addObject:dictionary];
            ALog(@"tipo.idx: %i, retain count: %i",tipo.idx, [tipo retainCount]);
            [tipo release];
            [dictionary release];
        }
        [restImage release];
        [pizzaImage release];
        [etnImage release];
        [brunchImage release];
    }

Solution

  • You are missing a retain on either r.tipo, or r.tipo.image.

    if they are both synthesized properties, check that the property declaration contains a retain.

    If you implemented you own getters and/or setters, check that you are retaining and releasing everything properly.

    Edit:

    I just saw the new code you posted. Your problem is you are releasing UIImages that your code does not own. Excluding all the conditional logic you basically do this:

    //Incorrect
    UIImage *myImagename = [UIImage imageNamed:@"foo.png"]
    yourclass.image = restImage;
    [myImagename release];
    

    This is incorrect because you never called alloc, copy or retain on the myImage object. [UIImage imageMamed] returns an autoreleased instance of a UIImage. It is the same as doing this (also incorrect):

    //Incorrect
    UIImage *myImagename = [[UIImage alloc] initWithImage:@"foo.png"] autorelease];
    yourclass.image = restImage;
    [myImagename release];
    

    You have two options. Either manage the release yourself:

    UIImage *myImagename = [UIImage alloc] initWithImage:@"foo.png"];
    yourclass.image = restImage;
    [myImagename release];
    

    Or let the autoreleased object do its thing:

    UIImage *myImagename = [UIImage imageNamed:@"foo.png"]
    yourclass.image = restImage;
    //Note: no release needed on yourClass.
    

    In your specific code, you can take the second approach and it will look like this:

    if (sqlite3_prepare_v2(db, queryStrTipo, -1, &query, NULL) == SQLITE_OK) {
            restImage = [UIImage imageNamed:@"rest.png"];
            pizzaImage = [UIImage imageNamed:@"pizza.png"];
            etnImage = [UIImage imageNamed:@"etnico.png"];
            brunchImage = [UIImage imageNamed:@"wineglass-blue.png"];
    
            while(sqlite3_step(query) == SQLITE_ROW) { 
                NSString *desc = [NSString stringWithUTF8String:(char *)sqlite3_column_text(query, 1)];
                tipo = [[Tipologia alloc] initWithIndex:sqlite3_column_int(query, 0)
                                                    description:desc ofType:kTipoRestUnknown];
                ALog(@"tipo.idx: %i, retain count: %i",tipo.idx, [tipo retainCount]);
                if ([desc compare:kTipoDescRestaurant options:NSCaseInsensitiveSearch] == NSOrderedSame) {
                    tipo.type = kTipoRestRestaurant;
                    tipo.image = restImage;
                } else 
                if ([desc compare:kTipoDescPizza options:NSCaseInsensitiveSearch] == NSOrderedSame) {
                    tipo.type = kTipoRestPizza;
                    tipo.image = pizzaImage;
                } else
                if ([desc compare:kTipoDescEtnico options:NSCaseInsensitiveSearch] == NSOrderedSame) {
                    tipo.type = kTipoRestEthnic;
                    tipo.image = etnImage;
                } else
                if ([desc compare:kTipoDescBrunch options:NSCaseInsensitiveSearch] == NSOrderedSame) {
                    tipo.type = kTipoRestBrunch;
                    tipo.image = brunchImage;
                } else
                if ([desc compare:kTipoDescRegionale options:NSCaseInsensitiveSearch] == NSOrderedSame) {
                    tipo.type = kTipoRestRegional;
                }
    
    
                dictionary = [[NSMutableDictionary alloc] initWithObjectsAndKeys:[NSNumber numberWithInt:tipo.idx], @"index", tipo.desc, @"desc", nil];
                [listaTipologie addObject:tipo];
                [listaTemp addObject:dictionary];
                ALog(@"tipo.idx: %i, retain count: %i",tipo.idx, [tipo retainCount]);
                [tipo release];
                [dictionary release];
            }
        }
    

    Remember, the golden rule of iOS memory management:

    If you use any method with the word copy, alloc, or new, you need to have a corresponding release.

    And, of course, Apple's Memory Management Programming Guide is the definitive resourse