关于iOS内存管理
Posted
技术标签:
【中文标题】关于iOS内存管理【英文标题】:about iOS memory management 【发布时间】:2012-04-25 04:56:34 【问题描述】:我有一个视图控制器显示一个表格视图,见下文:
//.h file
@interface CoreDataViewController : UIViewController<UITableViewDataSource, UITableViewDelegate>
//NSArray property
@property (retain, nonatomic) NSArray *arr;
@end
//.m file
- (void)viewDidLoad
//fetch data from core data, pass to arr property
//context is a instance of NSManagedObjectContext
arr = [context executeFetchRequest:request error:nil];
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
if (arr == nil)
return 0;
return [arr count]; //program stop here, nothing showed up in output console
xcode Profile(Instrument) 告诉我在 return [arr count] 处有一个僵尸对象; 我对后台发生的事情感到困惑,也许属性 arr 是由 ios 发布的,但是该属性在 .h 文件中有一个保留关键字。
如果 NSArray 像这样替换为 NSMutableArray,我发现一个解决方案可以解决这个问题:
@interface CoreDataViewController : UIViewController<UITableViewDataSource, UITableViewDelegate>
//change to NSMutableArray
@property (retain, nonatomic) NSMutableArray *arr;
@end
- (void)viewDidLoad
//convert NSArray to NSMutableArray
arr = [[context executeFetchRequest:request error:nil] mutableCopy];
mutableCopy 方法中是否有保留或自动释放?
【问题讨论】:
【参考方案1】:根据Basic Memory Management Rules,是的,mutableCopy 增加了保留计数。底线,名称以 alloc
、new
、copy
或 mutableCopy
开头的方法都返回一个具有 +1 保留计数的对象(即,它将为您保留,因此您取得所有权,并且在非 ARC 项目中,您负责手动发布它)。 executeFetchRequest
方法没有,所以它(可以)返回零保留计数,除非你拥有它,否则你不能依赖它,即你通过对你的保留做一些有效地 +1 保留计数拥有。
现在,很明显,您假设因为您将您的财产定义为保留财产,所以它将为您保留。但是您必须使用系统生成的 setter 来执行此操作,但您的初始代码示例不会执行此操作。相反,您自己直接访问 ivar,绕过 setter。如果你想获得所有权,增加你的属性所暗示的保留计数,你应该调用默认的 setter:
[self setArr:[context executeFetchRequest:request error:nil]];
或使用等效的点表示法:
self.arr = [context executeFetchRequest:request error:nil];
但是当您单独使用 ivar arr 时(没有点或 self setArr
语法),它绕过了 setter 方法。这不是一个好的做法,因为你没有做必要的保留。在这个例子中(因为你知道 arr 还没有值),理论上你可以这样做:
arr = [[context executeFetchRequest:request error:nil] retain];
但如果 arr 可能已经有一个指向另一个数组的指针,那么你真的想要:
[release arr];
arr = [[context executeFetchRequest:request error:nil] retain];
使用 setter 是安全的,并且可以避免这种愚蠢行为。看看Declared Properties,了解retain
的使用如何转换为什么setter 代码,我认为这可能更有意义。
更新:
顺便说一句,正如其他人指出的那样,虽然上面的代码解决了你的数组没有被保留的问题,但是一旦你成功地进行了保留,你必须记住在你的 dealloc 中释放它。
【讨论】:
非常漂亮和详细的 cmets @Robert Ryan。我习惯于用 C# 语法思考,所以忽略 ivar 和属性之间的区别。谢谢你的耐心回答,现在我知道了。 遗憾的是,Objective C 中 ivars 和属性之间的区别是不必要的模糊。更广泛地说,内存处理是这种语言的一个根本弱点。程序员真的不必担心这类事情。 ARC 让语言更接近于更合适的东西(如果你还没有看过它,你真的应该看过),但这种语言仍然感觉有点不合时宜。【参考方案2】:如果你使用 mutableCopy,你需要自己释放一个变量。它将保留计数增加 1,但不会减少。 可能你可以使用 like -
arr = [[[context executeFetchRequest:request error:nil] mutableCopy] autorelease];
【讨论】:
无意冒犯,但这种语法会遇到我认为@rock 试图解决的确切问题,即在调用他的tableView:numberOfRowsInSection
之前,变量已在他身上释放。他真的想保留变量(通过显式保留他将 ivar 设置为的内容,或使用属性的 setter 方法)。但对你来说,他不能忘记释放他在dealloc
或viewDidUnload
中保留的这个变量。
好吧,现在我记得 Apple 的指南中提到从复制方法返回的对象必须明确释放,谢谢你们俩
@rock - 如果您的问题得到解决,那么您可以接受适当的答案并关闭此问题。【参考方案3】:
是的,属性arr在.h文件中有一个retain关键字,但是你直接使用了ivar,你没有使用setter方法设置arr。所以它不会增加 arr 的保留计数,您应该将代码更改为:
//.m file
- (void)viewDidLoad
//fetch data from core data, pass to arr property
//context is a instance of NSManagedObjectContext
self.arr = [context executeFetchRequest:request error:nil];
在dealloc方法中释放arr
- (void)dealloc
[arr release];
[super dealloc];
无需将 NSArray 更改为 NSMutableArray
【讨论】:
谢谢@Tranz,我明白了。你们在理解内存管理、self 关键字和 getter setter 方面帮助了我很多。我永远不会犯这样的错误以上是关于关于iOS内存管理的主要内容,如果未能解决你的问题,请参考以下文章