2012-04-25 56 views
0

我有一个视图控制器显示一个表格图,见下文:了解iOS存储器管理

//.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的资料(仪表)告诉我有在返回僵尸对象[ARR计数]; 我很困惑在后台发生了什么,也许属性arr被iOS发布,但该属性在.h文件中有一个retain关键字。

我找到一个解决方案能解决这个问题,如果用的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方法?

回答

0

根据Basic Memory Management Rules,是的,mutableCopy增加了保留计数。底线,名称以allocnewnewcopymutableCopy开头的方法都会返回带有+1保留计数的对象(即,它将为您保留,因此您将拥有所有权, ARC项目,你负责手动释放它)。 executeFetchRequest方法不会,所以它(可以)返回一个零保留计数,除非您拥有它,否则您不能依赖它来保留它,即您通过保留自己的值来有效地+1保留计数。

现在,很显然,您假定因为您将您的财产定义为保留财产,它将保留给您。但是你必须使用系统生成的setter来做到这一点,但你最初的代码示例并没有这样做。相反,你直接亲自访问伊娃,绕过二传手。如果你想取得所有权,增加你的财产所蕴含的保留计数,你应该调用默认二传手:

[self setArr:[context executeFetchRequest:request error:nil]]; 

或使用点符号相当于:

self.arr = [context executeFetchRequest:request error:nil]; 

但是,当你使用ivar arr本身(没有点或self setArr语法),它绕过了setter方法。这不是很好的做法,因为你没有做必要的保留。在这个例子中(因为你知道,改编不已经有一个值),理论上你可以这样做:

arr = [[context executeFetchRequest:request error:nil] retain]; 

但如果ARR可能已经有一个指向另一个数组,你真的想:

[release arr]; 
arr = [[context executeFetchRequest:request error:nil] retain]; 

这是安全使用二传手,并避免一些这种愚蠢。看看Declared Properties,了解如何使用retain转化为什么setter代码,我认为这可能更有意义。

更新:

顺便说一句,因为其他人则指出,虽然上面的代码解决不被保留您的阵列的问题,但一旦你做成功的挽留,你要记住释放它你的dealloc。

+0

非常好的和详细的评论@罗伯特瑞恩。我习惯于用C#语法思考,所以忽略了ivar和property之间的区别。感谢您耐心回答,现在我知道了。 – rock 2012-04-25 07:54:40

+0

不幸的是,ivars和属性之间的Objective C区分是不必要的钝化。更广泛地说,内存处理是这种语言的一个基本弱点。程序员真的不应该担心这种事情。 [ARC](http://developer.apple.com/library/ios/#releasenotes/ObjectiveC/RN-TransitioningToARC/Introduction/Introduction.html)让语言更接近更合适的东西(如果你没有看过它,你真的应该),但语言仍然感觉有点不合时宜。 – Rob 2012-04-25 13:17:11

0

如果你正在使用mutableCopy,你需要自己释放一个变量。它将保留计数增加1,但不会降低。 可能是你可以像使用 -

arr = [[[context executeFetchRequest:request error:nil] mutableCopy] autorelease]; 
+0

没有冒犯,但是这个语法会遭受我认为@rock试图解决的确切问题,即在调用他的'tableView:numberOfRowsInSection'之前,变量会在他上面释放。他真的想要保留这个变量(要么通过明确保留他设置他的ivar或者使用该属性的setter方法)。但为了您的好处,他不能忘记释放他保留在他的'dealloc'或'viewDidUnload'中的这个变量。 – Rob 2012-04-25 06:36:49

+0

好吧,现在我记得苹果公司的指南提到,从复制方法返回对象必须明确释放,谢谢你们双方 – rock 2012-04-25 07:56:03

+0

@rock - 如果你的问题是固定的,那么你可以接受适当的答案,并可以关闭这个问题。 – rishi 2012-04-25 08:09:55

0

是,物业 ARR做在.h文件中一个保留关键字,但你直接使用伊娃,你没有使用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方法释放改编

- (void)dealloc 
{ 
    [arr release]; 
    [super dealloc]; 
} 

无需更改到的NSArray NSMutableArray

+0

谢谢@Tranz,我明白了。你们帮助我了解内存管理,自我关键字和getter setter。我不会犯这样的错误 – rock 2012-04-25 07:49:41