4

我有一个简单的 UIViewTable,通过故事板中的 UINavigationController push segue 实现了深入细节。有时会发生表视图控制器似乎被释放的情况,而我在详细视图中,因此我得到了著名的:

[MyViewController controllerWillChangeContent:]: message sent to deallocated instance

我解释得更好,我有一个 NSOperation 队列,它异步加载我的数据,并在完成后立即填充表。正确检索数据并填充表格。对于详细视图,我单击一个单元格并将 NSManagedObjectID 传递给 prepareForSegue 方法中的目标控制器。当我对详细视图进行更改时,非常随机地,获取的控制器松开了它的委托,或者看起来,作为控制器的委托本身被释放了。导致崩溃。

获取的结果控制器被声明为一个属性:

@property(nonatomic,strong) NSFetchedResultsController *fetchedResultsController;

然后这就是从 viewDidLoad 开始的一切工作方式。

- (void)viewDidLoad {
    [super viewDidLoad];
    [self loadDataAsynchronously];
}

-(void)loadDataAsynchronously {

    NSOperationQueue *queue = [NSOperationQueue new];
    NSInvocationOperation *operation = [[NSInvocationOperation alloc] initWithTarget:self
                                                                            selector:@selector(loadData)
                                                                              object:nil];

    [queue addOperation:operation];
}


-(void)loadData {

    NSFetchRequest *findAllEntities = [[NSFetchRequest alloc] init];
    [findAllEntities setEntity:ENTITY_DESC];

    NSSortDescriptor *sort = [[NSSortDescriptor alloc] initWithKey:@"created" ascending:YES];
    [findAllEntities setSortDescriptors:[NSArray arrayWithObject:sort]];
    [findAllEntities setFetchBatchSize:20];

    [NSFetchedResultsController deleteCacheWithName:@"MyCache"];
    if(self.fetchedResultsController==nil) {
        self.fetchedResultsController = [[NSFetchedResultsController alloc] 
                                            initWithFetchRequest:findAllPlants
                                            managedObjectContext:MOC
                                            sectionNameKeyPath:nil 
                                            cacheName:@"MyCache"];

        self.fetchedResultsController.delegate=self;
    }
    NSError *error=nil;
    if (![FRC performFetch:&error]) {
        exit(EXIT_FAILURE);
    }

    [self.dataTableView performSelectorOnMainThread:@selector(reloadData) withObject:nil waitUntilDone:YES];    

}

这段代码可以工作,而且大多数时候也可以在细节视图中工作,这被称为像这样的 segue:

-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {

    IP2SegueIdentifier segueIdentifier = [IP2Factory segueSolver:[segue identifier]];  
    MyDestinationViewController *dvc = [segue destinationViewController];
    NSIndexPath *indexPath = [TV indexPathForSelectedRow];
    dvc.entityID=[[self.fetchedResultsController objectAtIndexPath: indexPath] objectID];

}

并且目标控制器通过询问上下文正确获取实体ID并重建对象。然后,当我在细节视图控制器中时,我可以对实体进行更改,当我返回导航层次结构时,我会保存上下文。正是在这一点上,应用程序崩溃了,就在上下文保存时。不是那么频繁,而是时不时。因为获取的结果控制器识别更改并提交给已被释放的委托。

在这一点上我几乎没有疑问,我使用的是 iOS 5 和 ARC,所以编译器应该(几乎)完全控制 release 和 dealloc 方法。而且我还使用了一个带有简单导航层次结构的故事板,它应该保证整个以前的视图控制器链得到保留。

我还运行分析器进行内存泄漏/僵尸分析,但无法发现任何错误,相反我很高兴所有对象管理都很好。

在这一点上我没有太多猜测,所以请随时指出我可能忘记检查的东西,或者你在我的代码中看到的错误。

谢谢

4

1 回答 1

22

首先,关于 ARC 的说明。虽然 ARC 提供自动归零weak指针,但它不会使assign指针自动归零。为其委托NSFetchResultsController使用属性(请参阅 参考资料):assignNSFetchedResultsController.h

@property(nonatomic, assign) id< NSFetchedResultsControllerDelegate > delegate;

在解除分配之前,您仍然有责任清除自己作为代表。你通常这样做dealloc

- (void)dealloc {
  _fetchedResultsController.delegate = nil;
}

您可能还想摆脱您的fetchedResultsController参与viewWillDisappear:(包括将您自己作为代表)。通常,当您离开屏幕时,您不希望获取请求停留在周围。(如果这样做,您可能应该在模型对象中而不是在视图控制器中管理获取,因为视图控制器可以在其视图离开屏幕的任何时候消失。)

loadData的奇怪之处在于它创建了一个findAllEntities,但实际上使用了findAllPlants. 这是错字还是错误?如果findAllPlantsivar 中有单独的 fetch 请求,这也可能是您的问题的原因。

于 2012-02-09T17:42:13.283 回答