Question

OK, for two days now i have been trying to solve an error i have inside the cellForRowAtIndex method, let start by saying that i have tracked down the bug to this method, the error is [CFDictionary image] or [Not a Type image] message sent to deallocated instance.

I know about the debug flags, NSZombie, MallocStack, and others, they helped me narrow it down to this method and why, but I do not know how to solve besides a redesign of the app UI.

SO what am i trying to do, well for this block of code, displays a purchase detail, which contains items, the items are in there own section, now when in edit mode, there appears a cell at the bottom of the items section with a label of "Add new Item", and this button will present a modal view of the add item controller, item is added and the view returns to the purchase detail screen, with the just added item in the section just above the "add new Item" cell, the problem happens when i scroll the item section off screen and back into view the app crashes with EXC_BAD_ACCESS, or even if i don't scroll and instead hit the back button on the navBar, still the same error.

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath 
{
    UITableViewCell *cell   = nil;

    switch (indexPath.section) 
    {
        case PURCHASE_SECTION:
        {   
            static NSString *cellID = @"GenericCell";

            cell = [tableView dequeueReusableCellWithIdentifier:cellID];

            if (cell == nil)
            {
                cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue2 
                                               reuseIdentifier:cellID] autorelease];
                cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
            }

            switch (indexPath.row) 
            {
                case CATEGORY_ROW:
                    cell.textLabel.text         = @"Category:";
                    cell.detailTextLabel.text   = [self.purchase.category valueForKey:@"name"];
                    cell.accessoryType          = UITableViewCellAccessoryNone;
                    cell.editingAccessoryType   = UITableViewCellAccessoryDisclosureIndicator;

                    break;
                case TYPE_ROW:
                    cell.textLabel.text         = @"Type:";
                    cell.detailTextLabel.text   = [self.purchase.type valueForKey:@"name"];
                    cell.accessoryType          = UITableViewCellAccessoryNone;
                    cell.editingAccessoryType   = UITableViewCellAccessoryDisclosureIndicator;

                    break;
                case VENDOR_ROW:
                    cell.textLabel.text         = @"Payment:";
                    cell.detailTextLabel.text   = [self.purchase.vendor valueForKey:@"name"];
                    cell.accessoryType          = UITableViewCellAccessoryNone;
                    cell.editingAccessoryType   = UITableViewCellAccessoryDisclosureIndicator;

                    break;
                case NOTES_ROW:
                    cell.textLabel.text         = @"Notes";
                    cell.editingAccessoryType   = UITableViewCellAccessoryNone;

                    break;
                default:
                    break;
            }
            break;
        }
        case ITEMS_SECTION:
        {

            NSUInteger  itemsCount = [items count];

            if (indexPath.row < itemsCount) 
            {
                static NSString *itemsCellID = @"ItemsCell";

                cell = [tableView dequeueReusableCellWithIdentifier:itemsCellID];

                if (cell == nil)
                {
                    cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle 
                                                   reuseIdentifier:itemsCellID] autorelease];
                    cell.accessoryType = UITableViewCellAccessoryNone;
                }

                singleItem                  = [self.items objectAtIndex:indexPath.row];
                cell.textLabel.text         = singleItem.name;
                cell.detailTextLabel.text   = [singleItem.amount formattedDataDisplay];
                cell.imageView.image        = [singleItem.image image];

            } 
            else
            {
                static NSString *AddItemCellID = @"AddItemCell";

                cell = [tableView dequeueReusableCellWithIdentifier:AddItemCellID];

                if (cell == nil) 
                {
                    cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault 
                                                   reuseIdentifier:AddItemCellID] autorelease];
                    cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
                }

                cell.textLabel.text = @"Add Item";
            }
            break;
        }
        case LOCATION_SECTION:
        {
            static NSString *localID = @"LocationCell";

            cell = [tableView dequeueReusableCellWithIdentifier:localID];

            if (cell == nil)
            {
                cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle 
                                               reuseIdentifier:localID] autorelease];
                cell.accessoryType = UITableViewCellAccessoryNone;
            }

            cell.textLabel.text         = @"Purchase Location";
            cell.accessoryType          = UITableViewCellAccessoryDisclosureIndicator;
            cell.editingAccessoryType   = UITableViewCellAccessoryNone;
            break;
        }
        default:
            break;
    }
    return cell;
}

the singleItem is of Modal Type PurchaseItem for core data

now that i know what is causing the error, how do i solve it, I have tried everything that i know and some of what i dont know but still, no progress, please any suggestions as to how to solve this without redesign is my goal, perhaps there is an error i am doing that I cannot see, but if it's the nature of autorelease, than i will redesign.

UPDATE: adding the AddItemController

//
//  AddItemViewController.m
//  spendTrac
//
//  Created by iAm on 3/5/10.
//  Copyright 2010 heariamStudios. All rights reserved.
//

#import "AddItemViewController.h"
#import "Purchase.h"
#import "PurchaseItem.h"
#import "FormattedDataDisplay.h"
#import "ModalAlert.h"
#import "UtilityBelt.h"

@implementation AddItemViewController

@synthesize purchase, item, itemAmount, itemImage;
@synthesize amountPadBtn, nameAlertBtn, addImageBtn;
@synthesize amountLab, itemNameLab;
@synthesize itemImageView;


#pragma mark -
#pragma mark IBAction Methods

- (void)cancel:(id)sender 
{
    [self.navigationController popViewControllerAnimated:YES];
}

- (void)save:(id)sender 
{
    NSManagedObjectContext *context = purchase.managedObjectContext;

    if (!item)
    {
        itemImage   = [NSEntityDescription insertNewObjectForEntityForName:@"Image"         inManagedObjectContext:context];
        item        = [NSEntityDescription insertNewObjectForEntityForName:@"PurchaseItem"  inManagedObjectContext:context];
        [purchase addItemsObject:item];
        item.displayOrder = [NSNumber numberWithInteger:[purchase.items count]];

    }


    NSString        *stringAmt  = [UtilityBelt charStripper:self.amountLab.text];
    NSDecimalNumber *amount     = [NSDecimalNumber decimalNumberWithString:stringAmt];

    [itemImage setValue:self.itemImageView.image forKey:@"image"];

    item.image  = itemImage; 
    item.name   = itemNameLab.text;
    item.amount = amount;

    NSError *error = nil;
    if (![context save:&error]) 
    {
        NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
        abort();
    }

    [self.navigationController popViewControllerAnimated:YES];
}

- (void)amountPadButtonTapped:(id)sender
{
    AmountPad *amountPad = [[AmountPad alloc]initWithNibName:@"AmountPad" bundle:nil];
    amountPad.delegate = self;
    [self.navigationController presentModalViewController:amountPad animated:YES];
    [amountPad release];
}

- (void)nameAlertButtonTapped:(id)sender 
{
    NSString *answer = [ModalAlert ask:@"Name This Item" withTextPrompt:@"item name"];

    if (answer) 
    {
        self.itemNameLab.text = [NSString stringWithFormat:@"%@", answer];
    } 
    else 
    {
        [ModalAlert say:@"What, You Don't Know!"];
    }
}


- (void)photoButtonTapped:(id)sender
{
    UIImagePickerController *imagePicker = [[UIImagePickerController alloc] init];
    imagePicker.delegate        = self;
    imagePicker.allowsEditing   = YES;
    imagePicker.mediaTypes      = [UIImagePickerController availableMediaTypesForSourceType:imagePicker.sourceType];
    if ([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera])
    {
        imagePicker.sourceType = UIImagePickerControllerSourceTypeCamera;
    }
    else {
        imagePicker.sourceType = UIImagePickerControllerSourceTypePhotoLibrary;
    }
    [self presentModalViewController:imagePicker animated:YES];
    [imagePicker release];

}

- (void)imagePickerController:(UIImagePickerController *)picker 
        didFinishPickingImage:(UIImage *)selectedImage 
                  editingInfo:(NSDictionary *)editingInfo 
{
    // Create a thumbnail version of the image for the item object.
    CGSize size = selectedImage.size;
    CGFloat ratio = 0;
    if (size.width > size.height) {
        ratio = 277.3 / size.width;
    } else {
        ratio = 277.3 / size.height;
    }
    CGRect rect = CGRectMake(0.0, 0.0, ratio * size.width, ratio * size.height);

    UIGraphicsBeginImageContext(rect.size);
    [selectedImage drawInRect:rect];
    self.itemImageView.image = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();

    [self dismissModalViewControllerAnimated:YES];
}


- (void)imagePickerControllerDidCancel:(UIImagePickerController *)picker 
{
    [self dismissModalViewControllerAnimated:YES];
}

#pragma mark -
#pragma mark View Controller Methods

- (void)viewDidLoad 
{   
    if (self.item) 
    {
        self.itemImage              = self.item.image;
        self.itemImageView.image    = [self.item.image image];
        self.itemNameLab.text       = self.item.name;
        self.amountLab.text         = [self.item.amount formattedDataDisplay];
    }

    UINavigationItem *navigationItem = self.navigationItem;
    navigationItem.title = @"Item";

    UIBarButtonItem *cancelButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemCancel 
                                                                                  target:self 
                                                                                  action:@selector(cancel:)];
    self.navigationItem.leftBarButtonItem = cancelButton;
    [cancelButton release];

    UIBarButtonItem *saveButton = [[UIBarButtonItem alloc] initWithTitle:@"Save" 
                                                                   style:UIBarButtonSystemItemSave 
                                                                  target:self 
                                                                  action:@selector(save:)];
    self.navigationItem.rightBarButtonItem = saveButton;
    [saveButton release];
    [super viewDidLoad];
}

- (void)didReceiveMemoryWarning {
    // Releases the view if it doesn't have a superview.
    [super didReceiveMemoryWarning];

    // Release any cached data, images, etc that aren't in use.
}

- (void)viewDidUnload
{
    // Release any retained subviews of the main view.
    self.amountLab      = nil;
    self.itemNameLab    = nil;
    self.addImageBtn    = nil;
    self.nameAlertBtn   = nil;
    self.amountPadBtn   = nil;

    [super viewDidUnload];
}


#pragma mark -
#pragma mark AmountPadDelegate Method

- (void)amountPadDidPressDoneButton:(NSString *)amount
{
    NSDecimalNumber *itemAmt = [NSDecimalNumber decimalNumberWithString:amount];
    self.amountLab.text = [itemAmt formattedDataDisplay];
    [self dismissModalViewControllerAnimated:YES];
}



#pragma mark -
#pragma mark Memory Management

- (void)dealloc 
{
    [addImageBtn    release];   
    [nameAlertBtn   release];
    [amountPadBtn   release];

    [itemAmount     release];
    [purchase       release];
    [item           release];
    [itemImage      release];

    [itemImageView  release];

    [amountLab      release];
    [itemNameLab    release];
    [super dealloc];
}


@end
Was it helpful?

Solution

Looking at your save: method in AddItemViewController, I don't see where item is retained. It's autoreleased so it needs to be retained somewhere. It's possible that:

[purchase addItemsObject:item] 

retains it, but I'd have to see that code.

EDIT:

Add this to your save: method:

[item retain];

I don't see how item is retained. Since it's autoreleased item will be a bad pointer, leading to your error. And since you say the error manifests iself when you unload/load the view, and viewDidLoad: references item, I think this is a good possibility. I'm not completely sure since I don't know the class hierarch of purchase.

OTHER TIPS

Something having to do with your add item controller may be over-releasing the image in the singleItem you added. It then gets dealloced when the cell scrolls offscreen and is reused, then crashes when it's redisplayed. If that's true, the problem would lie outside -cellForRowAtIndexPath:.

If you don't see anything obvious, you could put continuing breakpoints on [PurchaseItem retain] and [PurchaseItem release] with the gdb command bt 3 (or so), and look at what's releasing it. You may instead have to look at retain and release of the image itself.

Did you try running the static code analyzer to see if it picks up a related memory management error?

One other thing: it seems odd to send the message -image to an image. What exactly is the class of a singleItem.image ?

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top