NSFetchedResultsController 随机崩溃并出现 NSInvalidArgumentException
我有一个 iOS 应用程序,它使用 Core Data 将一些通过互联网下载的数据临时存储到数据库中。这些数据用于通过 NSFetchedResultsController 填充 UITableView。下面是生成控制器的代码(放置在 UIViewController 中):
- (NSFetchedResultsController *)fetchedResultsController {
if (fetchedResultsController != nil) {
return fetchedResultsController;
}
// Fetch results from database
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:@"Order" inManagedObjectContext:[self.appDelegate ordersObjectContext]];
[fetchRequest setEntity:entity];
NSSortDescriptor *sort = [[NSSortDescriptor alloc] initWithKey:@"patronName" ascending:YES];
[fetchRequest setSortDescriptors:[NSArray arrayWithObject:sort]];
// Prefetch size: tweak this to improve performance
[fetchRequest setFetchBatchSize:20];
NSFetchedResultsController *theFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:[self.appDelegate ordersObjectContext] sectionNameKeyPath:@"patronNameInitial" cacheName:nil];
self.fetchedResultsController = theFetchedResultsController;
fetchedResultsController.delegate = self;
[entity release];
[sort release];
[fetchRequest release];
[theFetchedResultsController release];
return fetchedResultsController;
}
在我的应用程序工作流程中,包装该控制器的表视图必须多次卸载和加载,问题就来了。一旦上下文由我的“订单”实体填充,如果我使用结果控制器来回切换到视图,则在从视图堆栈中推送和弹出几次后,应用程序崩溃并出现以下错误:
Fatal error. NSInvalidArgumentException. Entity name must not be nil.. User info: (null)
奇怪的是,对于控制器必须显示的一组给定数据,崩溃在每次应用程序运行时恰好同时发生。示例:我总是在第六次显示带有数据的视图。
但是如果我的对象上下文同时没有改变,这怎么可能呢?为什么(例如)我第六次收到此错误时它可以工作 5 次?
请注意,我的托管对象上下文和模型由我的应用程序委托拥有,并且永远不会卸载,因此如果我不显式修改它们,数据不应随时间变化。
也许问题是因为我实际上并没有将“订单”保存到数据库中,而是将它们保存在内存中的对象上下文中?我这样做是因为当应用程序停止时,下载的数据必须被擦除。
编辑1:我尝试在崩溃发生之前正确记录该行,并且
NSLog(@"%@", [[self.appDelegate managedObjectModel] entities]);
导致应用程序因 EXC_BAD_ACCESS 崩溃,因此问题出现在 ManagedObjectModel 中。
编辑2:我尝试用超过1500个实体填充上下文,并且在加载和卸载视图30次后我没有任何崩溃。似乎只有当托管对象上下文仅包含很少的实体时,我才会崩溃!这太奇怪了。
I have an iOS application which uses Core Data to temporarily store some data downloaded by internet into a database. These data are used to populate an UITableView through a NSFetchedResultsController. Here's the code (placed into the UIViewController) which generates the controller:
- (NSFetchedResultsController *)fetchedResultsController {
if (fetchedResultsController != nil) {
return fetchedResultsController;
}
// Fetch results from database
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:@"Order" inManagedObjectContext:[self.appDelegate ordersObjectContext]];
[fetchRequest setEntity:entity];
NSSortDescriptor *sort = [[NSSortDescriptor alloc] initWithKey:@"patronName" ascending:YES];
[fetchRequest setSortDescriptors:[NSArray arrayWithObject:sort]];
// Prefetch size: tweak this to improve performance
[fetchRequest setFetchBatchSize:20];
NSFetchedResultsController *theFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:[self.appDelegate ordersObjectContext] sectionNameKeyPath:@"patronNameInitial" cacheName:nil];
self.fetchedResultsController = theFetchedResultsController;
fetchedResultsController.delegate = self;
[entity release];
[sort release];
[fetchRequest release];
[theFetchedResultsController release];
return fetchedResultsController;
}
In my application work flow, the table view to which this controller is wrapped has to be unloaded and loaded several time, and here comes the problem. Once the context is populated by my "Order" entities, if I switch back and forth to the view with the results controller, after several times of pushes and pops from the view stack the application crashes with the following error:
Fatal error. NSInvalidArgumentException. Entity name must not be nil.. User info: (null)
The curious thing is that, for a given set of data that the controller has to display, the crash occurs EXACTLY at the same time in every application run. Example: always at the sixth time I display the view with the data.
But how is this possible if my object context does not change in the meanwhile? Why (example) it works 5 times the sixth time I get this error?
Please note that my managed object context and model are owned by my application delegate and never unloads, so the data should not change over time if I don't explicitly modify them.
Maybe the issue is because I don't actually save the "Orders" to the database but I keep them in memory, in the object context? I do this because when the application stops, the downloaded data has to be wiped.
EDIT 1: I tried to log right the line before the crash occurs, and
NSLog(@"%@", [[self.appDelegate managedObjectModel] entities]);
Is causing the application to crash with EXC_BAD_ACCESS, so the problem occurs in the managedObjectModel.
EDIT 2: I tried to fill the context with more than 1500 entities, and I after loading and unloading the view for 30 times I didn't have any crash. It seems I get crashes only when the managed object context contains only few entities! This is so weird.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
您不应该释放实体
,因为您从未对它调用过 alloc,所以您不拥有它。
You shouldn't be releasing entity
Since you never called alloc on it, you don't own it.
我注意到您没有缓存部分信息。为什么不呢?设置
cacheName:@"Root"
以利用NSFetchResultController
的优势。请注意,只有当 FetchRequestController 的设置不可变时,这才有意义。据我在您发布的代码中看到的,这就是您在这里所拥有的。因此,您应该继续缓存您的部分信息。您的数据只会在第一次启动时加载到缓存中,并产生一点开销,但稍后只会观察表视图中的变化。
我试图思考这是否也可能是您问题的原因,但只有我不确定的模糊假设,所以我将把它们留在这里。但尝试改变这一点...
I noticed that you are not caching section informations. Why not ? Set
cacheName:@"Root"
to use the advantage of theNSFetchResultController
.Note that this only makes sense if the settings of the
FetchRequestController
are immutable. As far as I can see in your posted code, that's what you have here. So you should go ahead and cache your section informations. Your data will only be loaded into the cache on first launch with a little bit of overhead but later on only observes changes in your table view.I am trying to think if that could also be a reason for your problem but only have vague assumptions I am not sure about, so I will leave them out here. But try changing that...