我对iphone开发很新,我在应用程序中遇到了一个奇怪的崩溃。实际上,在我模拟了内存警告后,我的应用程序总是崩溃。我每次都可以重现这种行为并设法隔离出错误的行:)。
我正在使用自定义UITableViewController,提供自定义UITableViewCells。
@implementation CustomTableViewController
// [...]
- (UITableViewCell *)tableView:(UITableView *)tv cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = @"MyTableViewCell";
UITableViewCell *cell = nil;
if ([indexPath row] < [dataList childCount])
{
cell = [tv dequeueReusableCellWithIdentifier:CellIdentifier];
if (nil == cell)
{
cell = [[[KpowUITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:CellIdentifier] autorelease];
KUICustomView* customView = [[KUICustomView alloc]initWithFrame:CGRectZero];
[(KpowUITableViewCell*)cell setFrontView:customView];
[customView release];
}
KUICustomView* cView = [(KpowUITableViewCell*)cell frontView];
[cView setDataObject:[dataList getChildAtIndex:[indexPath row]]]; // The crash happens in this function
}
// [...]
这是我为单元格视图设置自定义数据对象的功能:
-(void)setDataObject:(DataObject *)do
{
[do retain];
[dataObject release];
dataObject = do;
NSString* defaultPath = [NSString stringWithFormat:@"%@/default_image.png", [[NSBundle mainBundle] resourcePath]];
UIImage* defaultImage = [[UIImage alloc] initWithContentsOfFile:defaultPath];
[self setImage: defaultImage];//[UIImage imageNamed:@"default_image"]]; // The crash happens in this function
[defaultImage release];
// [...]
最后,这就是神奇发生的地方:
-(void)setImage:(UIImage *)img
{
[img retain];
NSLog(@"setImage : old image > %@/%@/%i", [image description], [[image class]description], [image retainCount]);
[image release]; // CRASH EXC_BAD_ACCESS
image = img;
[self setNeedsDisplay];
}
因此,在正常情况下一切正常。但是,如果我模拟内存警告,滚动我的UITableView并调用所有这些函数,应用程序崩溃。如果我删除[图像发布],没有崩溃(但'海有内存泄漏')。 NSLog的输出总是类似于:
setImage : old image > <UIImage: 0x4b54910>/UIImage/1
我真的看不出我做错了什么,或者我能做些什么来解决这个问题。 这是Xcode调试器的截图...
http://img30.imageshack.us/i/debuggerscreen.png/
欢迎任何帮助。 提前致谢
编辑1: @bbum Build and Analyze向我展示了一些不相关的警告,但仍然有用。甚至没有看到它在那里
还有另一个我设置图像的地方。在setDataObject
中,图片只是一个占位符。我异步启动真实图像的下载,然后在requestDidFinishLoad
中恢复。方法如下:
- (void)requestDidFinishLoad:(KURLRequest*)request
{
if (request == currentRequest)
{
UIImage* img = [[UIImage alloc] initWithData:[request data]];
if (nil != img)
[self setImage:img];
[img release];
}
if (currentRequest == request)
currentRequest = nil;
[request release];
}
我使用NSZombie Detection运行仪器,结果似乎指向另一个方向。这是一个截图:
http://img13.imageshack.us/i/zombieinstrument.jpg/
我还不太清楚该怎么做,但调查进展如此:)
答案 0 :(得分:4)
[image release]; // CRASH EXC_BAD_ACCESS
您的回溯表明您使用dealloc
UIImage
方法崩溃了。你在某处过度释放了image
。
首先,尝试“构建和分析”,看看它是否会阻止任何有用的警告。修复它们。
接下来,打开Zombie Detection尝试重现问题。它可能提供线索。
请注意,@property
“仅仅”方便了声明一个setter / getter方法对(或者其中一个)。无论您使用[foo setImage:bar]
还是直接声明方法,@property
都是完全等效的。同样,foo.image = bar;
与[foo setImage:bar];
完全相同。
最后,是所有代码处理您的image
吗?你如何处理低内存警告?
此外,如果你的二传手不致电setNeedsDisplay:
,你会感觉更好。使用简单的@property
和@synthesize
setter / getter。然后,当您拨打设定者时,请在该线路上呼叫setNeedsDisplay:
。这使得设置UI的业务与确定何时需要显示相隔离。
啊哈!你的僵尸东西非常有用。特别是,您似乎过早地释放了URLConnection,然后导致NSData过早释放。这可能很容易成为问题的根源,或者至少应该在尝试修复此问题之前修复。
答案 1 :(得分:1)
尤里卡! 我终于找到了我做错了什么。 当图像被异步加载时,它使用来自用于缓存的自定义对象的数据,存储在缓存管理器中。发出内存警告时,缓存管理器会释放所有内容,从内存中销毁缓存对象。这是我的dealloc在我的“cachable对象”中的样子:
-(void)dealloc
{
// [...]
[data dealloc];
// [...]
}
是的,我明确地调用了dealloc ...当然,当UIImage想要在数据上发布自己的指针时,它就失败了......
我觉得很蠢^^。 (我总是很难调试自己的程序,因为我有时假设部分代码为“OK”,我甚至不认为看那里......)
底线: NSZombie非常有用(感谢@bbum)找出真正的罪魁祸首。并且从不(?)明确地调用dealloc 。
(无论如何要“关闭”这个问题?
答案 2 :(得分:0)
您可能正在释放图像以响应内存警告而不将其设置为nil。因此,当您调用setImage:
时,您正在释放已释放的对象,因此崩溃。尝试类似:
- (void)viewDidUnload {
[super viewDidUnload];
[image release]; image = nil;
}
或image
被声明为属性
- (void)viewDidUnload {
[super viewDidUnload];
self.image = nil;
}
答案 3 :(得分:0)
专门解决@ sabby的误解:
-(void)setImage:(UIImage *)img
{
[img retain]; // img rc +1
image = img;
if(image)
{
[image release]; // img rc -1
}
// image set, but not retained by `self`
}
最终结果?不保留项集的setter方法。将其与:
结合起来- initWithImage:anImage
{
if (self=[super init]) {
image = [anImage retain];
}
return self;
}
上述设置器将泄漏传递给init
的原始图像,过度释放传递给setImage:
的任何图像,如果应用程序存活的时间足够长,很可能会崩溃在:
- (void) dealloc
{
[image release];
[super dealloc];
}
答案 4 :(得分:-1)
这样做,可能会帮助你
-(void)setImage:(UIImage *)img
{
[img retain];
NSLog(@"setImage : old image > %@/%@/%i", [image description], [[image class]description], [image retainCount]);
// CRASH EXC_BAD_ACCESS
image = img;
if(image)
{
[image release];
}
[self setNeedsDisplay];
}
我没有检查过我的自己但是你崩溃的原因是图片的发布。