【发布时间】:2011-10-03 06:54:29
【问题描述】:
我在使用具有简单一对一关系的 NSFetchedResultsController 进行插入时遇到了一些问题。当我创建一个与目标对象具有一对一关系的新 Source 对象时,它似乎调用 - [(void)controller:(NSFetchedResultsController *)controller didChangeObject ... ] 两次,同时使用 NSFetchedResultsChangeInsert 和 NSFetchedResultsChangeUpdate 类型,这会导致 tableview 在更新后立即显示不准确的数据。
我可以通过一个简单的示例重新创建它,该示例基于 XCode 在基于导航的 CoreData 应用程序中生成的标准模板项目。该模板创建一个带有 timeStamp 属性的 Event 实体。我想为这个事件添加一个新的实体“标签”,它只是与实体的一对一关系,这个想法是每个事件都有一个来自某个标签列表的特定标签。我在 Core Data 编辑器中创建了从事件到标签的关系,以及从标签到事件的反向关系。然后我为 Event 和 Tag 生成 NSManagedObject 子类,它们非常标准:
@interface Event : NSManagedObject {
@private
}
@property (nonatomic, retain) NSDate * timeStamp;
@property (nonatomic, retain) Tag * tag;
and
@interface Tag : NSManagedObject {
@private
}
@property (nonatomic, retain) NSString * tagName;
@property (nonatomic, retain) NSManagedObject * event;
然后我在启动时用一些数据预先填充了标签实体,以便我们可以在插入新事件时从标签中进行选择。在 AppDelegate 中,在返回 persistentStoreCoordinator 之前调用它:
NSManagedObjectContext *context = [self managedObjectContext];
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:@"Tag" inManagedObjectContext:context];
[fetchRequest setEntity:entity];
NSError *error = nil;
NSArray *fetchedObjects = [context executeFetchRequest:fetchRequest error:&error];
//check if Tags haven't already been created. If not, then create them
if (fetchedObjects.count == 0) {
NSLog(@"create new objects for Tag");
Tag *newManagedObject1 = [NSEntityDescription insertNewObjectForEntityForName:@"Tag" inManagedObjectContext:context];
newManagedObject1.tagName = @"Home";
Tag *newManagedObject2 = [NSEntityDescription insertNewObjectForEntityForName:@"Tag" inManagedObjectContext:context];
newManagedObject2.tagName = @"Office";
Tag *newManagedObject3 = [NSEntityDescription insertNewObjectForEntityForName:@"Tag" inManagedObjectContext:context];
newManagedObject3.tagName = @"Shop";
}
[fetchRequest release];
if (![context save:&error])
{
NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
abort();
}
现在,我更改了 insertNewObject 代码,将标签添加到我们要插入的 Event 属性中。对于这个例子,我只是从 fetchedObjects 列表中选择第一个:
- (void)insertNewObject
{
NSManagedObjectContext *context = [self.fetchedResultsController managedObjectContext];
NSEntityDescription *entity = [[self.fetchedResultsController fetchRequest] entity];
Event *newManagedObject = [NSEntityDescription insertNewObjectForEntityForName:[entity name] inManagedObjectContext:context];
// If appropriate, configure the new managed object.
// Normally you should use accessor methods, but using KVC here avoids the need to add a custom class to the template.
[newManagedObject setValue:[NSDate date] forKey:@"timeStamp"];
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entityTag = [NSEntityDescription entityForName:@"Tag" inManagedObjectContext:context];
[fetchRequest setEntity:entityTag];
NSError *errorTag = nil;
NSArray *fetchedObjects = [context executeFetchRequest:fetchRequest error:&errorTag];
if (fetchedObjects.count > 0) {
Tag *newtag = [fetchedObjects objectAtIndex:0];
newManagedObject.tag = newtag;
}
// Save the context.
NSError *error = nil;
if (![context save:&error])
{
NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
abort();
}
}
我现在想看到反映这些更改的 tableview,所以我让 UITableViewCell 输入 UITableViewCellStyleSubtitle 并更改 configureCell 以在详细文本标签中显示 tagName:
- (void)configureCell:(UITableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath
{
Event *managedObject = [self.fetchedResultsController objectAtIndexPath:indexPath];
cell.textLabel.text = [[managedObject valueForKey:@"timeStamp"] description];
cell.detailTextLabel.text = managedObject.tag.tagName;
}
现在一切就绪。当我调用 insertNewObject 时,它似乎可以很好地创建第一行,但第二行是第一行的副本,即使时间戳应该相隔几秒钟:
当我上下滚动屏幕时,它会刷新行,然后以正确的时间显示正确的结果。当我单步执行代码时,核心问题出现了:插入新行似乎调用了 [(NSFetchedResultsController *)controller didChangeObject ...] 两次,一次用于插入,一次用于更新。我不确定为什么会调用更新。关键是:如果我删除事件和标签之间的反比关系,插入开始工作就好了!只有插入被调用,行不重复,一切正常。
那么导致 NSFetchedResultsController 委托方法被调用两次的反向关系是什么?在这种情况下,我应该没有他们就生活吗?我知道如果未指定反函数,XCode 会发出警告,这似乎是个坏主意。我在这里做错了吗?这是已知解决方法的一些已知问题吗?
谢谢。
【问题讨论】:
-
对此有何更新?我正在寻找类似的问题。我收到 1 个上下文更改通知,并且我的获取结果控制器委托被击中两次。
标签: objective-c core-data nsfetchedresultscontroller