0

我有一个视图控制器显示一个表格视图,见下文:

//.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方法中是否有保留或自动释放?

4

3 回答 3

0

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

arr = [[[context executeFetchRequest:request error:nil] mutableCopy] autorelease];
于 2012-04-25T05:02:50.337 回答
0

是的,属性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

于 2012-04-25T06:28:37.113 回答
0

根据Basic Memory Management Rules,是的, mutableCopy 增加了保留计数。底线,名称以alloc, new,开头的方法copy,或mutableCopy全部返回一个具有 +1 保留计数的对象(即,它将为您保留,因此您获得所有权,并且在非 ARC 项目中,您'负责手动释放它)。该executeFetchRequest方法没有,因此它(可以)返回零保留计数,除非您拥有它,否则您不能依赖它存在,即您通过自己的保留有效地 +1 保留计数。

现在,很明显,您假设因为您将您的财产定义为保留财产,它将为您保留。但是您必须使用系统生成的 setter 来执行此操作,但您的初始代码示例不会执行此操作。相反,您自己直接访问 ivar,绕过 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 中释放它。

于 2012-04-25T06:32:41.533 回答