TableViewCell 自动释放错误
好的,两天来我一直在尝试解决 cellForRowAtIndex 方法中的错误,首先说我已经追踪到此方法的错误,错误是 [CFDictionary image] 或 [Not a Type image]消息发送到已释放的实例。
我了解调试标志、NSZombie、MallocStack 等,它们帮助我将范围缩小到此方法及其原因,但除了重新设计应用程序 UI 之外,我不知道如何解决。
那么我想做什么,对于这段代码,显示一个购买详细信息,其中包含项目,这些项目位于自己的部分中,现在在编辑模式下,项目部分的底部会出现一个单元格,其中包含“添加新商品”标签,此按钮将呈现添加商品控制器的模式视图,添加商品后视图返回到购买详细信息屏幕,刚刚添加的商品位于“添加新商品”上方的部分中Item”单元格,当我将项目部分滚动到屏幕外并返回视图时,应用程序因 EXC_BAD_ACCESS 崩溃,或者即使我不滚动而是点击导航栏上的后退按钮,仍然出现相同的错误。
- (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 是核心数据的模态类型 PurchasingItem
现在我知道是什么导致了错误,我该如何解决它,我已经尝试了我所知道的所有内容和一些我不知道的内容,但仍然没有进展,请提出任何建议如何在不重新设计的情况下解决这个问题是我的目标,也许我正在做一个我看不到的错误,但如果这是自动释放的本质,那么我会重新设计。
更新:添加 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
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
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
看看 AddItemViewController 中的 save: 方法,我看不到项目被保留在哪里。它是自动释放的,因此需要将其保留在某个地方。有可能:
保留它,但我必须查看该代码。
编辑:
将其添加到您的 save: 方法中:
我不知道如何保留项目。由于它的自动释放项将是一个错误的指针,从而导致您的错误。既然你说当你卸载/加载视图和 viewDidLoad: 引用项时错误就会显现出来,我认为这是一个很好的可能性。我不完全确定,因为我不知道购买的类层次结构。
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:
retains it, but I'd have to see that code.
EDIT:
Add this to your save: method:
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.
与您的添加项目控制器有关的事情可能会过度释放您添加的 singleItem 中的图像。然后,当单元格滚动到屏幕外并重新使用时,它会被释放,然后在重新显示时崩溃。如果这是真的,问题将出在 -cellForRowAtIndexPath: 之外。
如果您没有看到任何明显的东西,您可以使用 gdb 命令 bt 3 (或类似的命令)在 [PurchaseItemretain] 和 [PurchaseItemrelease] 上放置连续断点,然后查看释放它的内容。相反,您可能必须查看图像本身的保留和释放。
您是否尝试运行静态代码分析器以查看它是否发现相关的内存管理错误?
另一件事:将消息 -image 发送到图像似乎很奇怪。 singleItem.image 的类到底是什么?
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 ?