gpt4 book ai didi

ios - 具有合并更改的 CoreData 和 NSFetchedResultsController

转载 作者:行者123 更新时间:2023-11-29 10:48:45 25 4
gpt4 key购买 nike

我正在处理我的第一个 CoreData 应用程序,我遇到了几个问题 - 我觉得我做错了什么。

在主线程上,我有一个带有 NSFetchedResultsController 的 UITableView,使用以下代码创建:

- (NSFetchedResultsController *)newFetchedResultsControllerWithSearch:(NSString *)searchString
{
NSSortDescriptor *sort = [[[NSSortDescriptor alloc] initWithKey:@"order" ascending:YES] autorelease];
NSArray* sortDescriptors = @[sort];

NSPredicate *filterPredicate = [NSPredicate predicateWithFormat:@"contactId != 1"];

/*
Set up the fetched results controller.
*/
// Create the fetch request for the entity.
NSFetchRequest *fetchRequest = [[[NSFetchRequest alloc] init] autorelease];
// Edit the entity name as appropriate.
NSEntityDescription *callEntity = [NSEntityDescription entityForName:@"ContactDB" inManagedObjectContext:[[AppManager sharedAppManager] managedObjectContext]];
[fetchRequest setEntity:callEntity];

NSMutableArray *predicateArray = [NSMutableArray array];
if(searchString.length)
{
// your search predicate(s) are added to this array
[predicateArray addObject:[NSPredicate predicateWithFormat:@"name CONTAINS[cd] %@", searchString]];
// finally add the filter predicate for this view
if(filterPredicate)
{
filterPredicate = [NSCompoundPredicate andPredicateWithSubpredicates:[NSArray arrayWithObjects:filterPredicate, [NSCompoundPredicate orPredicateWithSubpredicates:predicateArray], nil]];
}
else
{
filterPredicate = [NSCompoundPredicate orPredicateWithSubpredicates:predicateArray];
}
}

[fetchRequest setPredicate:filterPredicate];

// Set the batch size to a suitable number.
[fetchRequest setFetchBatchSize:20];

[fetchRequest setSortDescriptors:sortDescriptors];

// Edit the section name key path and cache name if appropriate.
// nil for section name key path means "no sections".
NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest
managedObjectContext:[[AppManager sharedAppManager] managedObjectContext]
sectionNameKeyPath:nil
cacheName:nil];
aFetchedResultsController.delegate = self;

NSError *error = nil;
if (![aFetchedResultsController performFetch:&error])
{
/*
Replace this implementation with code to handle the error appropriately.

abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development. If it is not possible to recover from the error, display an alert panel that instructs the user to quit the application by pressing the Home button.
*/
NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
abort();
}

return aFetchedResultsController;
}

我正在进行一些后台下载以更新显示在该 UITableView 中的数据。根据我在网上找到的信息,我正在创建新的 MOC 来进行这些更改,稍后我会将其与我的主线程 MOC 合并。

- (void)managedObjectContextDidSave:(NSNotification *)notification {
dispatch_async(dispatch_get_main_queue(), ^{
if(notification.object != [[AppManager sharedAppManager] managedObjectContext]) {
[[[AppManager sharedAppManager] managedObjectContext] mergeChangesFromContextDidSaveNotification:notification];
}
});
}

对于合并委托(delegate),我使用标准的 Apple 函数。

- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller
{
UITableView *tableView = controller == self.fetchedResultsController ? self.tableView : self.searchDisplayController.searchResultsTableView;
[tableView beginUpdates];
}

- (void)controller:(NSFetchedResultsController *)controller
didChangeSection:(id <NSFetchedResultsSectionInfo>)sectionInfo
atIndex:(NSUInteger)sectionIndex
forChangeType:(NSFetchedResultsChangeType)
{
UITableView *tableView = controller == self.fetchedResultsController ? self.tableView : self.searchDisplayController.searchResultsTableView;
switch(type)
{
case NSFetchedResultsChangeInsert:
[tableView insertSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationFade];
break;

case NSFetchedResultsChangeDelete:
[tableView deleteSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationFade];
break;
}
}

- (void)controller:(NSFetchedResultsController *)controller
didChangeObject:(id)anObject
atIndexPath:(NSIndexPath *)theIndexPath
forChangeType:(NSFetchedResultsChangeType)type
newIndexPath:(NSIndexPath *)newIndexPath
{
UITableView *tableView = controller == self.fetchedResultsController ? self.tableView : self.searchDisplayController.searchResultsTableView;
switch(type)
{
case NSFetchedResultsChangeInsert:
[tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationFade];
break;

case NSFetchedResultsChangeDelete:
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:theIndexPath] withRowAnimation:UITableViewRowAnimationFade];
break;

case NSFetchedResultsChangeUpdate:
[self fetchedResultsController:controller configureCell:[tableView cellForRowAtIndexPath:theIndexPath] atIndexPath:theIndexPath];
break;

case NSFetchedResultsChangeMove:
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:theIndexPath] withRowAnimation:UITableViewRowAnimationFade];
[tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath]withRowAnimation:UITableViewRowAnimationFade];
break;
}
}

- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller
{
UITableView *tableView = controller == self.fetchedResultsController ? self.tableView : self.searchDisplayController.searchResultsTableView;
[tableView endUpdates];
}

问题 1:通知通过并且合并在 UITableView 中可见,但问题是过滤对合并不起作用。正如您在我的过滤器中看到的那样,我跳过了 contactId = 1,但是在完成合并时,此联系人会出现在 UITableView 中。

我仍然不确定是否有更好的方法来响应在另一个线程上对数据库所做的更改 - 例如重新加载 TableView 和重新获取 UITableView 数据。

问题 2:当我在重新加载时快速滚动时它崩溃并显示消息:

Terminating app due to uncaught exception 'NSObjectInaccessibleException', reason: 'CoreData could not fulfill a fault for '0x19762a40 

只有当我在后台线程中删除并且它出现在负责创建 UITableViewCell 的代码中时才会发生这种情况 - 它在访问其中一个属性时崩溃。

编辑

作为我所做的解决方法:

- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller
{
UITableView *tableView = controller == self.fetchedResultsController ? self.tableView : self.searchDisplayController.searchResultsTableView;
[tableView endUpdates];

[controller performFetch:nil];
[tableView reloadData];
}

所以我手动执行 Fetch 和 reloadingData 以再次过滤条目。

下面是我构建 CoreData 相关对象的方式:

- (NSManagedObjectModel *)managedObjectModel
{
if(_managedObjectModel != nil) {
return _managedObjectModel;
}
NSURL *modelURL = [[NSBundle mainBundle] URLForResource:@"Model" withExtension:@"momd"];
self.managedObjectModel = [[[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL] autorelease];
return _managedObjectModel;
}

- (NSManagedObjectContext *)managedObjectContext
{
if (_managedObjectContext != nil) {
return _managedObjectContext;
}

self.managedObjectContext = [self createManagedObjectContextWithConcurrencyType:NSMainQueueConcurrencyType];
return _managedObjectContext;
}

- (NSManagedObjectContext *) createManagedObjectContextWithConcurrencyType:(NSManagedObjectContextConcurrencyType)concurrrencyType
{
NSManagedObjectContext* managedObjectContext = [[[NSManagedObjectContext alloc] initWithConcurrencyType:concurrrencyType] autorelease];

if(concurrrencyType == NSMainQueueConcurrencyType)
{
[managedObjectContext setPersistentStoreCoordinator:[self persistentStoreCoordinator]];
}
else
{
[managedObjectContext setParentContext:self.managedObjectContext];
}

return managedObjectContext;
}

这里是更新联系人的代码:

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSManagedObjectContext* context = [[AppManager sharedAppManager] createManagedObjectContextWithConcurrencyType:NSPrivateQueueConcurrencyType];

//clear database
NSArray* allContacts = [context fetchObjectsForEntityName:@"ContactDB"];

if([allContacts count] > 0)
{
for (ContactDB* contact in allContacts)
{
[context deleteObject:contact];
}
}

NSArray* responseContacts = responseObject[@"contacts"];
[responseContacts enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
NSMutableDictionary* contactDict = [NSMutableDictionary dictionaryWithDictionary:obj];
ContactDB* contact = [[[ContactDB alloc] initWithDictionary:contactDict andContext:context] autorelease];
contact.order = [NSNumber numberWithInt:[order indexOfObject:contact.contactId]];
}];

[context save:nil];
[[[AppManager sharedAppManager] managedObjectContext] performBlock:^{
[[[AppManager sharedAppManager] managedObjectContext] save:nil];
}];

dispatch_async(dispatch_get_main_queue(), ^{
[[NSNotificationCenter defaultCenter] postNotificationName:NOTIFICATION_LOADING_STOPPED object:self userInfo:nil];
});
});

最佳答案

经过 2 天的环顾四周,我从头开始准备了一个更简单的问题示例,我终于找到了一个问题。我仍然不明白为什么它从加载的数据库中工作而不是更新但我改变了:

NSPredicate *filterPredicate = [NSPredicate predicateWithFormat:@"contactId != 1"];

收件人:

NSPredicate *filterPredicate = [NSPredicate predicateWithFormat:@"contactId != %@", @"1"];

我知道第一个是不正确的,因为它不是 NSString 而我的 contactId 是 NSString,但为什么它在我加载应用程序时工作 - 当核心数据从磁盘加载时。对于带有 != 1 的合并和更新 NSPredicate 不起作用。

也许它会对某人有所帮助 - 确保您尊重类型的谓词。

关于ios - 具有合并更改的 CoreData 和 NSFetchedResultsController,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21340395/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com