Iphone 每当以特定方法访问NSManagedObject时,应用程序就会崩溃

Iphone 每当以特定方法访问NSManagedObject时,应用程序就会崩溃,iphone,objective-c,core-data,crash,Iphone,Objective C,Core Data,Crash,我在iPhone应用程序的UIViewController中有一个方法(在UINavigationController中),每当在ViewController视图的表中选择一行时,就会调用该方法。在这个方法中,我访问存储在实例字段dreamsArray中的“Dream”数组,该字段包含我数据库中的NSManagedObjects。我可以用其他方法访问这个数组中的对象,但似乎每当我试图用这个特定的方法从这个数组中检索或修改检索到的对象时,程序就会崩溃 以下是dreamsArray的创建方式:

我在iPhone应用程序的UIViewController中有一个方法(在UINavigationController中),每当在ViewController视图的表中选择一行时,就会调用该方法。在这个方法中,我访问存储在实例字段dreamsArray中的“Dream”数组,该字段包含我数据库中的NSManagedObjects。我可以用其他方法访问这个数组中的对象,但似乎每当我试图用这个特定的方法从这个数组中检索或修改检索到的对象时,程序就会崩溃

以下是dreamsArray的创建方式:

    dreamsArray = [[NSMutableArray alloc] init];

    [self managedObjectContext];

    NSFetchRequest *request = [[NSFetchRequest alloc] init];
    NSEntityDescription *entity = [NSEntityDescription entityForName:@"Dream" inManagedObjectContext:managedObjectContext];
    [request setEntity:entity];

    NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"title" ascending:NO];
    NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil];
    [request setSortDescriptors:sortDescriptors];
    [sortDescriptors release]; [sortDescriptor release];

    NSError *error;
    NSMutableArray *mutableFetchResults = [[managedObjectContext executeFetchRequest:request error:&error] mutableCopy];
    if ( mutableFetchResults == nil )
        NSLog(@"oh noes! no fetch results DreamsTabController:45");

    dreamsArray = [mutableFetchResults mutableCopy];
    [mutableFetchResults release];
    [request release];
查询dreamsArray及其对象的实例:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    static NSString *cellID = @"Cell";

    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellID];
    if ( cell == nil )
        cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:cellID] autorelease];

    Dream *dream = (Dream *)[dreamsArray objectAtIndex:indexPath.row];

    cell.textLabel.text = [dream title];
    cell.detailTextLabel.text = @"foo!";

    [dream release];

    return cell;
}
以及存在所有问题的方法:

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
    Dream *dream = (Dream *)[dreamsArray objectAtIndex:indexPath.row];
    // BOOM - crashes right here
    EditDreamController *edit = [[EditDreamController alloc] initWithNibName:@"EditDream" bundle:nil];
    edit.dream = [[NSArray alloc] initWithObjects:dream.dreamContent, nil];
    [navigationController pushViewController:edit animated:YES];
    [dream release];
    [edit release];
}
在查询dreamsArray后,应用程序立即崩溃

即使在此方法中调用一个简单的
NSLog(@“%@”,dream.title)
,也会导致崩溃。这里会出什么问题

Dream *dream = (Dream *)[dreamsArray objectAtIndex:indexPath.row];

cell.textLabel.text = [dream title];
cell.detailTextLabel.text = @"foo!";

[dream release];
你不应该释放梦想。数组控制住了它。
-tableView:didselectrowatinexpath:
方法也是如此。最有可能的情况是,该对象已被释放足够多的时间以释放,从而在数组中留下一个悬空引用

最终结果

撞车

此外,您的第一位代码具有:

dreamsArray = [[NSMutableArray alloc] init];

[self managedObjectContext];

NSFetchRequest *request = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:@"Dream" inManagedObjectContext:managedObjectContext];
[request setEntity:entity];

NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"title" ascending:NO];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil];
[request setSortDescriptors:sortDescriptors];
[sortDescriptors release]; [sortDescriptor release];

NSError *error;
NSMutableArray *mutableFetchResults = [[managedObjectContext executeFetchRequest:request error:&error] mutableCopy];
if ( mutableFetchResults == nil )
    NSLog(@"oh noes! no fetch results DreamsTabController:45");

dreamsArray = [mutableFetchResults mutableCopy];
这里有一些混乱的代码

(1) 为什么要将dreamsArray设置为空的可变数组,然后将其重置为引用获取请求结果的可变副本?您正在泄漏空的可变数组

(2) 调用
[self-managedObjectContext]
,但不对返回值执行任何操作。然后直接使用
managedObjectContext
。只要到处使用
[self-managedObjectContext]
。开销可以忽略不计

(3) 您创建一个保留的提取请求并将其分配给
请求
,但从不释放它。另一个内存泄漏

(4) 为什么要复制
mutableFetchResults
两次?这没有任何意义(并且正在导致另一次泄漏)

总之,我建议您重新阅读有关Objective-C内存管理的文档

你不应该释放梦想。数组控制住了它。
-tableView:didselectrowatinexpath:
方法也是如此。最有可能的情况是,该对象已被释放足够多的时间以释放,从而在数组中留下一个悬空引用

最终结果

撞车

此外,您的第一位代码具有:

dreamsArray = [[NSMutableArray alloc] init];

[self managedObjectContext];

NSFetchRequest *request = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:@"Dream" inManagedObjectContext:managedObjectContext];
[request setEntity:entity];

NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"title" ascending:NO];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil];
[request setSortDescriptors:sortDescriptors];
[sortDescriptors release]; [sortDescriptor release];

NSError *error;
NSMutableArray *mutableFetchResults = [[managedObjectContext executeFetchRequest:request error:&error] mutableCopy];
if ( mutableFetchResults == nil )
    NSLog(@"oh noes! no fetch results DreamsTabController:45");

dreamsArray = [mutableFetchResults mutableCopy];
这里有一些混乱的代码

(1) 为什么要将dreamsArray设置为空的可变数组,然后将其重置为引用获取请求结果的可变副本?您正在泄漏空的可变数组

(2) 调用
[self-managedObjectContext]
,但不对返回值执行任何操作。然后直接使用
managedObjectContext
。只要到处使用
[self-managedObjectContext]
。开销可以忽略不计

(3) 您创建一个保留的提取请求并将其分配给
请求
,但从不释放它。另一个内存泄漏

(4) 为什么要复制
mutableFetchResults
两次?这没有任何意义(并且正在导致另一次泄漏)


总而言之,我建议您重新阅读有关Objective-C内存管理的文档。

以下代码更短、更高效、更易于阅读,并且没有第一个块中的六个左右内存泄漏:

NSFetchRequest *request = [[NSFetchRequest alloc] init];
[request setEntity:[NSEntityDescription entityForName:@"Dream" inManagedObjectContext:[self managedObjectContext]]];

static NSArray *sortDescriptors = nil;
if (!sortDescriptors)
    sortDescriptors = [[NSArray alloc] initWithObject:[[[NSSortDescriptor alloc] initWithKey:@"title" ascending:NO] autorelease]];
[request setSortDescriptors:sortDescriptors];

NSError *error = nil;
NSArray *fetchResults = [managedObjectContext executeFetchRequest:request error:&error];
if (!fetchResults)
    NSLog(@"oh noes! no fetch results DreamsTabController:45, error %@", error);
[request release];

dreamsArray = [NSMutableArray arrayWithArray:fetchResults];
此方法被重写为更小且不会过度释放,从而导致崩溃:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath;
{
    static NSString *cellID = @"Cell";

    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellID] ? : [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:cellID] autorelease];

    Dream *dream = [dreamsArray objectAtIndex:indexPath.row];
    cell.textLabel.text = dream.title;
    cell.detailTextLabel.text = @"foo!";

    return cell;
}
这种方法有一个“梦”的过度释放和一个NSArray上的漏洞,因此也有一个“梦”实例

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath;
{
    EditDreamController *editDreamController = [[EditDreamController alloc] initWithNibName:@"EditDream" bundle:nil];

    Dream *dream = [dreamsArray objectAtIndex:indexPath.row];
    editDreamController.dream = [NSArray arrayWithObjects:dream.dreamContent];

    [navigationController pushViewController:editDreamController animated:YES];
    [editDreamController release];
}
不清楚EditDreamController上的实例变量在接受数组时是否是单数的——如果您真的可以设置多个实例变量,那么它应该是“dreams”


-Wil

以下代码更短、更高效、更易于阅读,并且没有第一个块中的六个左右内存泄漏:

NSFetchRequest *request = [[NSFetchRequest alloc] init];
[request setEntity:[NSEntityDescription entityForName:@"Dream" inManagedObjectContext:[self managedObjectContext]]];

static NSArray *sortDescriptors = nil;
if (!sortDescriptors)
    sortDescriptors = [[NSArray alloc] initWithObject:[[[NSSortDescriptor alloc] initWithKey:@"title" ascending:NO] autorelease]];
[request setSortDescriptors:sortDescriptors];

NSError *error = nil;
NSArray *fetchResults = [managedObjectContext executeFetchRequest:request error:&error];
if (!fetchResults)
    NSLog(@"oh noes! no fetch results DreamsTabController:45, error %@", error);
[request release];

dreamsArray = [NSMutableArray arrayWithArray:fetchResults];
此方法被重写为更小且不会过度释放,从而导致崩溃:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath;
{
    static NSString *cellID = @"Cell";

    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellID] ? : [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:cellID] autorelease];

    Dream *dream = [dreamsArray objectAtIndex:indexPath.row];
    cell.textLabel.text = dream.title;
    cell.detailTextLabel.text = @"foo!";

    return cell;
}
这种方法有一个“梦”的过度释放和一个NSArray上的漏洞,因此也有一个“梦”实例

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath;
{
    EditDreamController *editDreamController = [[EditDreamController alloc] initWithNibName:@"EditDream" bundle:nil];

    Dream *dream = [dreamsArray objectAtIndex:indexPath.row];
    editDreamController.dream = [NSArray arrayWithObjects:dream.dreamContent];

    [navigationController pushViewController:editDreamController animated:YES];
    [editDreamController release];
}
不清楚EditDreamController上的实例变量在接受数组时是否是单数的——如果您真的可以设置多个实例变量,那么它应该是“dreams”


-Wil

次要观点:我个人会使用[[fetchResults mutableCopy]autorelease],而不是[NSMutableArray arrayWithArray:fetchResults],因为理论上,fetchResults可以有一个优化的mutableCopy方法(在将来,即使目前没有)。我从来没有使用过-mutableCopy,因为我不相信它。一个原因是,我几乎记不起“mutableCopy”是不是很深(不应该是这样),如果我记不住,我怎么能指望其他阅读我代码的人记住呢?另一个原因是,如果以这种方式对其进行优化,那么使用它使快速枚举安全(因为我正在修改基数组)仍然是不安全的。另外,如果基数组是一个不可靠的NSArray子类,现在我知道它只是一个普通的子类。你不记得它是否是深度副本的论点是一个薄弱的论点,因为你可以对arrayWithArray说同样的话。这并不是要偏离你更一般的观点:如果你不容易记住某件事,你就不应该这样做(例如,我忘记了&和| | |之间的优先顺序,所以我使用括号)。我个人认为,记住mutableCopy是否做深度复制并不属于这一类;这是你应该知道的。而且,我不明白你的第二点。如果您不能使用优化版本的mutableCopy来确保快速枚举的安全性,那么它将被破坏。我想到的一个可能的优化是,由于获取结果可能来自数据库,因此它可能是一个“懒惰”对象,制作一个可变副本可以更快地批量加载所有数据