我有一个简单的 UIViewTable,在 Storyboard中使用 UINavigationController 推送 segue 实现了下钻细节。 有时,当我在详细 View 中时, TableView Controller 似乎被释放了,因此我得到了著名的:
[MyViewController controllerWillChangeContent:]: message sent to deallocated instance
我解释得更好,我有一个 NSOperation 队列,它异步加载我的数据,并在它刚完成时立即填充表。正确检索数据并填充表格。 对于详细 View ,我单击一个单元格并将 NSManagedObjectID 传递给 prepareForSegue 方法中的目标 Controller 。当我对详细 View 进行更改时,非常随机地获取的 Controller 松开了它的委托(delegate),或者看起来,作为 Controller 的委托(delegate)本身被释放了。导致崩溃。
获取的结果 Controller 被声明为一个属性:
@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 的详细 View :
-(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];
}
目标 Controller 正确获取实体 ID 并通过询问上下文重建对象。 然后,当我在详细 View Controller 中时,我可以对实体进行更改,当我返回到导航层次结构时,我会进行上下文保存。 正是在这一点上,应用程序崩溃,就在上下文保存时。不是经常,而是时不时。 因为获取的结果 Controller 识别更改并提交给已经释放的委托(delegate)。
此时我几乎没有怀疑,我使用的是 iOS 5 和 ARC,因此编译器应该(几乎)完全控制 release 和 dealloc 方法。我还使用了一个带有简单导航层次结构的 Storyboard ,这应该保证保留整个以前的 View Controller 链。
我还运行了内存泄漏/僵尸分析的探查器,但没有发现任何错误,相反,我很高兴所有的对象管理都很好。
目前我没有太多猜测,所以请随时指出我可能忘记检查的地方,或者你在我的代码中看到的错误地方。
谢谢
最佳答案
首先,关于 ARC 的注释。虽然 ARC 提供自动归零 weak
指针,但它不会使 assign
指针自动归零。 NSFetchResultsController
为其委托(delegate)使用了一个assign
属性(参见NSFetchedResultsController.h
):
@property(nonatomic, assign) id< NSFetchedResultsControllerDelegate > delegate;
在解除分配之前,您仍然有责任清除自己作为委托(delegate)人的身份。您通常在 dealloc
中执行此操作:
- (void)dealloc {
_fetchedResultsController.delegate = nil;
}
您可能还想在 viewWillDisappear:
中删除您的 fetchedResultsController
(包括删除您自己作为委托(delegate))。通常,当您不在屏幕上时,您不希望获取请求停留在周围。 (如果这样做,您可能应该在模型对象中而不是在 View Controller 中管理获取,因为 View Controller 可以在其 View 离开屏幕时随时消失。)
您的 loadData
很奇怪,因为它创建了一个 findAllEntities
,但实际上使用了 findAllPlants
。这是打字错误还是错误?如果在 ivar 中有一个单独的 findAllPlants
提取请求,这也可能是您出现问题的原因。
关于objective-c - NSFetchedResultsController 保留对 Storyboard中已释放委托(delegate) Controller 的引用,导致崩溃,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9215176/