注意:这特别适用于非 ARC内存管理。
由于这有很多观点,并且检查过的答案适当地指出“代码显示严重缺乏关于内存管理在 Objective-C 中如何工作的知识”,但没有人指出具体错误,我想我会添加一个触及他们的答案。
关于调用方法,我们必须记住的基线级别规则:
让我们看一下 OP 代码的每一行:
-(UIImage *) downloadImageToFile {
我们开始了一种新方法。在这样做的过程中,我们开始了一个新的上下文,每个创建的对象都生活在这个上下文中。请记住这一点。下一行:
NSURL * url = [[NSURL alloc] initWithString: self.urlField.text];
我们拥有url
:那里的单词alloc告诉我们我们拥有该对象的所有权,并且我们需要自己释放它。如果我们不这样做,那么代码将泄漏内存。
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
我们不拥有paths
:没有使用四个神奇的词,所以我们没有所有权,绝对不能自己释放。
NSString *documentsDirectory = [paths objectAtIndex:0];
我们不拥有documentsDirectory
:没有魔法=没有所有权。
[paths release]
回顾几行,我们看到我们不拥有路径,因此当我们尝试访问不再存在的内容时,此版本将导致 EXC_BAD_ACCESS 崩溃。
NSString * path = [documentsDirectory stringByAppendingString:@"/testimg.png"];
我们不拥有path
:没有魔法=没有所有权。
NSData * data = [[NSData alloc] initWithContentsOfURL:url];
我们拥有:那里的allocdata
一词告诉 use 我们拥有该对象的所有权,并且我们需要自己释放它。如果我们不这样做,那么代码将泄漏内存。
以下两行不创建或释放任何内容。然后是最后一行:
}
该方法结束,因此变量的上下文已经结束。查看代码,我们可以看到我们拥有url
和data
,但没有发布它们中的任何一个。因此,每次调用此方法时,我们的代码都会泄漏内存。
这个NSURL
物体url
不是很大,所以我们可能永远不会注意到泄漏,虽然它仍然应该被清理,但没有理由泄漏它。
NSData
对象data
是 png 图像,并且可能非常大;每次调用此方法时,我们都会泄漏对象的整个大小。想象一下,每次绘制表格单元格时都会调用它:不会花费很长时间使整个应用程序崩溃。
那么我们需要做些什么来解决这些问题呢?这很简单,我们只需要在不再需要对象时立即释放它们,通常是在最后一次使用它们之后:
-(UIImage *) downloadImageToFile {
// We own this object due to the alloc
NSURL * url = [[NSURL alloc] initWithString: self.urlField.text];
// We don't own this object
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
// We don't own this object
NSString *documentsDirectory = [paths objectAtIndex:0];
//[paths release] -- commented out, we don't own paths so can't release it
// We don't own this object
NSString * path = [documentsDirectory stringByAppendingString:@"/testimg.png"];
// We own this object due to the alloc
NSData * data = [[NSData alloc] initWithContentsOfURL:url];
[url release]; //We're done with the url object so we can release it
[data writeToFile:path atomically:YES];
[data release]; //We're done with the data object so we can release it
return [[UIImage alloc] initWithContentsOfFile:path];
//We've released everything we owned so it's safe to leave the context
}
有些人喜欢在方法结束时上下文关闭之前立即释放所有内容。在这种情况下,两者[url release];
都会[data release];
出现在右}
大括号之前。我发现如果我尽快发布它们,代码会更清晰,当我稍后仔细检查它时,我会清楚地知道我在哪里完成了对象。
总结一下:我们拥有使用alloc
、new
、copy
或retain
在方法调用中创建的对象,因此必须在上下文结束之前释放它们。我们不拥有任何其他东西,也绝不能释放它们。
¹这四个词实际上并没有什么神奇之处,它们只是苹果公司创建相关方法的人们一贯使用的提醒。如果我们为自己的类创建自己的初始化或复制方法,那么在其适当的方法中包含单词 alloc、new、copy 或 retain 是我们的责任,如果我们不在名称中使用它们,那么我们'将需要自己记住所有权是否已经过去。