Restkit/CoreData - 更新实体的属性不会更新 UITableView

Posted

技术标签:

【中文标题】Restkit/CoreData - 更新实体的属性不会更新 UITableView【英文标题】:Restkit/CoreData - Updating an entity's attribute does not update UITableView 【发布时间】:2014-12-17 22:59:13 【问题描述】:

我有一个实体Country,其中包含一个属性downloaded,其默认值为0,并且未被RestKit 映射。我希望能够根据这个下载的属性将我的 tableView 分组。一切都按预期工作,直到我尝试自己以编程方式更改下载的值。代码如下:

我试图设置值的地方 - 我的上下文是通过 AppDelegate 传递给控制器​​的 mainQueueManagedObjectContext

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath

    [tableView deselectRowAtIndexPath:indexPath animated:YES];
    Country *country = [self.fetchedResultsController objectAtIndexPath:indexPath];
    [FGDataCalls downloadFieldGuide:country.countryId];

    //Set up to get the thing you want to update
    NSFetchRequest * request = [[NSFetchRequest alloc] init];
    [request setEntity:[NSEntityDescription entityForName:@"Country" inManagedObjectContext:self.managedObjectContext]];
    [request setPredicate:[NSPredicate predicateWithFormat:@"countryId = %@", country.countryId]];

    NSError *error = nil;
    country = [[self.managedObjectContext executeFetchRequest:request error:&error] lastObject];

    if (error) 
        NSLog(@"Error getting the country from core data: %@", error);
    

    country.downloaded = 1;

    error = nil;
    if (![self.managedObjectContext save:&error]) 
        NSLog(@"Saving changes to country failed: %@", error);
    

FetchedResultsController:

- (NSFetchedResultsController *)fetchedResultsController

    if (_fetchedResultsController != nil) 
        return _fetchedResultsController;
    

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

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

    // Sort using the name property.
    NSSortDescriptor *sortDownloaded = [[NSSortDescriptor alloc] initWithKey:@"downloaded" ascending:NO];
    NSSortDescriptor *sortName = [[NSSortDescriptor alloc] initWithKey:@"name" ascending:YES];
    [fetchRequest setSortDescriptors:@[sortDownloaded, sortName]];

    // Use the sectionIdentifier property to group into sections.
    _fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:self.managedObjectContext sectionNameKeyPath:@"downloaded" cacheName:nil];
    _fetchedResultsController.delegate = self;
    self.fetchedResultsController = _fetchedResultsController;

    NSError *error = nil;
    if (![self.fetchedResultsController 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.
        NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
        abort();
    

    return _fetchedResultsController;

初始化 Restkit:

//Enable RestKit logging
//RKLogConfigureByName("RestKit/ObjectMapping", RKLogLevelInfo);
//RKLogConfigureByName("RestKit/CoreData", RKLogLevelTrace);
//RKLogConfigureByName("RestKit/Network", RKLogLevelTrace);

// Initialize RestKit
RKObjectManager *objectManager = [RKObjectManager managerWithBaseURL:[NSURL URLWithString:BaseURLString]];

// Initialize managed object store
NSError *error = nil;
NSURL *modelURL = [NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"FGDataModel" ofType:@"momd"]];
NSManagedObjectModel *managedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];
RKManagedObjectStore *managedObjectStore = [[RKManagedObjectStore alloc] initWithManagedObjectModel:managedObjectModel];

BOOL success = RKEnsureDirectoryExistsAtPath(RKApplicationDataDirectory(), &error);
if (!success) 
    RKLogError(@"Failed to create Application Data Directory at path '%@': %@", RKApplicationDataDirectory(), error);


objectManager.managedObjectStore = managedObjectStore;

NSString *path = [RKApplicationDataDirectory() stringByAppendingPathComponent:@"FGDataModel.sqlite"];
NSPersistentStore *persistentStore = [managedObjectStore addSQLitePersistentStoreAtPath:path fromSeedDatabaseAtPath:nil withConfiguration:nil options:nil error:&error];

if (! persistentStore) 
    RKLogError(@"Failed adding persistent store at path '%@': %@", path, error);


// Configure a managed object cache to ensure we do not create duplicate objects
managedObjectStore.managedObjectCache = [[RKInMemoryManagedObjectCache alloc] initWithManagedObjectContext:managedObjectStore.persistentStoreManagedObjectContext];

[managedObjectStore createManagedObjectContexts];

// Set the default store shared instance
[RKManagedObjectStore setDefaultStore:managedObjectStore];

// Setup our object mappings
[FGDataCalls setupObjectMappings:managedObjectStore withObjectManager:objectManager];

// Save the MOC
AppDelegate *appDelegate = [[UIApplication sharedApplication]delegate];
appDelegate.managedObjectContext = managedObjectStore.mainQueueManagedObjectContext;

此时我已经尝试以多种不同的方式保存上下文,包括块,但我还没有让 fetchedResultsController 识别更新,甚至调用controllerWillChangeContent:controllerDidChangeContent:。我已经使用 MOC 的 deleteObject: 方法成功删除了一行,所以我的上下文处于正确的状态。在设置下载的属性后,我什至可以从我的 fetchedResultsController 中获取对象并查看它是否正确,但表永远不会更新。此外,在我更改属性后强制重新加载表不会产生任何结果。我知道这是一个具体案例,但希望其他人也遇到过同样的问题,或者可以看出我哪里出错了。

谢谢!

【问题讨论】:

“已下载”属性是 NSNumber 还是标量类型?从 .xcdatamodel 生成 NSManagedObject 时,您是否勾选了“使用原始数据类型的标量属性”? 请出示您的 FRC 委托方法! 我可以添加它们,但它们目前都只是 Apple 的样板方法。当我对 FRC 进行更改时,它们不会被调用 哦,哇,Michal 非常感谢。我不知道我不能使用标量类型,Core Data 也没有表明由于我试图保存 NSI​​nteger 而失败。更改为NSNumber,一切正常。 【参考方案1】:

保存时不应该这样做

error = nil;
if (![self.managedObjectContext save:&error]) 
    NSLog(@"Saving changes to country failed: %@", error);

你应该这样做

error = nil;
if (![self.managedObjectContext saveToPersistentStore:&error]) 
    NSLog(@"Saving changes to country failed: %@", error);

也就是说,假设 FRC 连接到相同的上下文,那么它应该在保存更改之前看到更改。您也没有显示您的 FRC 委托方法,因此可能存在问题。

【讨论】:

我也尝试过,但结果相同。我的 FRC 委托方法此时都只是样板的 Apple 方法,但是当我更改 FRC 中的对象时,它们都不会被访问。 那我看不出有什么明显的错误。您是否自动生成了托管对象子类? 在上面的cmets中解决了。我不知道 Core Data 只适用于 NSNumber - 我现在永远不会忘记它。

以上是关于Restkit/CoreData - 更新实体的属性不会更新 UITableView的主要内容,如果未能解决你的问题,请参考以下文章

我可以防止 RestKit+CoreData 覆盖本地实体更改吗?

RestKit / Core Data:远程删除的实体不会从 Core Data 中删除

RestKit + CoreData,理解 RKManagedObjectRequestOperation

RestKit CoreData 0.20.3 - 映射完成后保存 MOC 之前的额外检查

Restkit + Coredata - NSFetchedResultsControllerDelegate 回调不会单独为更新操作触发

RestKit/CoreData 未更新 - 创建重复项