每当以某种方法访问NSManagedObjects时,应用程序崩溃

时间:2009-09-20 00:17:32

标签: iphone objective-c core-data crash

我的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)也会导致崩溃。这里可能出现什么问题?

2 个答案:

答案 0 :(得分:27)

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

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];

这种方法被重写为更小而不是过度释放的梦想,这导致了crashers:

- (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上的实例变量采用数组时它是奇异的 - 如果你真的可以设置多个数组,它应该是'dream'。

-Wil

答案 1 :(得分:8)

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

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

[dream release];

你不应该释放-dream。阵列掌握它。 -tableView:didSelectRowAtIndexPath:方法也是如此。最有可能的是,该对象已被释放足够的时间以便被释放,在数组中留下悬空引用。

最终结果?

崩溃。

此外,您的第一段代码包含:

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)您创建一个保留的提取请求并将其分配给request,但永远不会释放它。另一个内存泄漏。

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

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