TableViewCell 自动释放错误

Posted

技术标签:

【中文标题】TableViewCell 自动释放错误【英文标题】:TableViewCell autorelease error 【发布时间】:2010-03-08 22:24:26 【问题描述】:

好的,这两天我一直在尝试解决我在 cellForRowAtIndex 方法中遇到的错误,首先说我已经找到了这个方法的错误,错误是 [CFDictionary image] 或 [Not a类型图像] 消息发送到解除分配的实例。

我知道调试标志、NSZombie、MallocStack 等,它们帮助我将范围缩小到这种方法以及为什么,但除了重新设计应用程序 UI 之外,我不知道如何解决。

所以我想做什么,对于这个代码块,显示一个购买详细信息,其中包含项目,项目在自己的部分中,现在在编辑模式下,在底部出现一个单元格带有“添加新项目”标签的项目部分,此按钮将显示添加项目控制器的模式视图,项目被添加并且视图返回到购买详细信息屏幕,刚刚添加的项目在正上方的部分中“添加新项目”单元格,当我将项目部分滚动到屏幕外并返回查看应用程序崩溃时出现问题.

- (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;

singleItem 是核心数据的 Modal 类型 PurchaseItem

现在我知道是什么导致了错误,我该如何解决它,我已经尝试了我所知道的所有内容以及一些我不知道的内容,但仍然没有进展,请就如何在不重新设计的情况下解决这个问题提出任何建议是我的目标,也许我正在做一个我看不到的错误,但如果这是自动释放的性质,我会重新设计。

更新:添加 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

【问题讨论】:

请贴出整个方法。 【参考方案1】:

查看 AddItemViewController 中的 save: 方法,我看不到项目保留在哪里。它是自动发布的,因此需要保留在某个地方。有可能:

[purchase addItemsObject:item] 

保留它,但我必须查看该代码。

编辑:

将此添加到您的保存:方法:

[item retain];

我看不到项目是如何保留的。由于它的自动发布项目将是一个错误的指针,从而导致您的错误。而且由于您说在卸载/加载视图时会出现错误,并且 viewDidLoad: 引用项目,我认为这是一个很好的可能性。我不完全确定,因为我不知道购买的类层次结构。

【讨论】:

好的,singleItem 的类型为 PurchaseItem,它是一个 coreData 实体,是的,它是 PurchaseDetailController 的一个属性,我确实有 self.singleItem,但为了测试而删除了它。我还添加了一条保留消息 self.singleItem = [self.items objectAtIndex:indexPath.row] retain];但仍然是同样的错误。如果你有时间看看,我可以给你发两个类 PurchaseDetail 和 AddItem,让我知道 当你添加一个项目时,它会被添加到 items 数组中吗? 只需将其他类的相关代码添加到您的问题中即可。 好的,为你加课,再次感谢你花时间帮助我,非常感谢。 addItemsObject:item 是自动生成coredata的方法,那我怎么保留呢?【参考方案2】:

与您的添加项目控制器有关的事情可能是过度释放您添加的单项中的图像。然后,当单元格滚动到屏幕外并被重用时,它会被释放,然后在重新显示时崩溃。如果这是真的,那么问题就出在 -cellForRowAtIndexPath: 之外。

如果您没有看到任何明显的东西,您可以使用 gdb 命令 bt 3(左右)在 [PurchaseItem retain] 和 [PurchaseItem release] 上放置连续断点,并查看释放它的内容。您可能不得不查看图像本身的保留和释放。

您是否尝试运行静态代码分析器来查看它是否发现了相关的内存管理错误?

另一件事:将消息 -image 发送到图像似乎很奇怪。 singleItem.image 的类到底是什么?

【讨论】:

singleItem 是核心数据模型类 PurchaseItem,“image”是该实体中的相关属性。我有一个名为“Image”的实体,以及与 PurchaseItem 实体的反向关系。现在“[singleItem.image image]”返回一个 UIimage,但 singleItem.image 返回一个“Image”类型。我希望这会有所帮助,也感谢回复,你提到了一些我没有尝试过的东西,我对这个东西还是新手,所以我会尝试静态代码分析器,看看会发生什么。我也将数据保存到添加项目控制器的上下文中,所以我应该能够在那个控制器中释放它,是的,不是 静态分析器没有返回任何问题,如果您有任何其他建议我是开放的,问题必须在 [singleItem.image image] 调用中,因为当我离开时没有错误,但我还是太新,看不到解决方案,但是我会像精力充沛的兔子一样继续前进:),但我可能需要朝着正确的方向前进;) 我如何在 [PurchaseItem release] 上设置断点,因为在断点窗口中,我使用了一个符号,它是灰色的,这条消息 [PurchaseItem release] 不在我的代码中。那么如何测试以查看何时调用

以上是关于TableViewCell 自动释放错误的主要内容,如果未能解决你的问题,请参考以下文章

tableviewcell 点击 设置

如何修复 TableViewCell 约束冲突?

如何使 tableviewcell 高度自动增长

iOS tableViewCell嵌套tableview,cell刷新紊乱解决办法

自定义 tableviewcell 和自动布局

具有指定大小的自动释放 UIImages 的奇怪 malloc 错误