CoreData:相同的谓词(IN)在保存操作后返回不同的获取结果
Posted
技术标签:
【中文标题】CoreData:相同的谓词(IN)在保存操作后返回不同的获取结果【英文标题】:CoreData: Same predicate (IN) returns different fetched results after a Save operation 【发布时间】:2014-06-06 15:28:00 【问题描述】:重大更新:
我创建了一个全新的项目来模拟这个问题,它确实一直出现!
这是我的数据模型:
Task
具有指向Person
的一对多关系。
@interface Person : NSManagedObject
@property (nonatomic, retain) NSString * personId;
@property (nonatomic, retain) NSString * name;
@end
//
@class Person;
@interface Task : NSManagedObject
@property (nonatomic, retain) NSNumber * taskId;
@property (nonatomic, retain) NSSet *watchers;
@end
@interface Task (CoreDataGeneratedAccessors)
- (void)addWatchersObject:(Person *)value;
- (void)removeWatchersObject:(Person *)value;
- (void)addWatchers:(NSSet *)values;
- (void)removeWatchers:(NSSet *)values;
@end
现在,我创建两个人,p1 和 p2,以及两个任务,1001 和 1002:
- (void)viewDidLoad
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
Person *p1 = [self insertOrUpdatePerson:@"111" withName:@"aaa"];
Person *p2 = [self insertOrUpdatePerson:@"222" withName:@"bbb"];
[self insertOrUpdateTask:1001 withWatcher:p1];
[self insertOrUpdateTask:1001 withWatcher:p2];
[self insertOrUpdateTask:1002 withWatcher:p1];
[self insertOrUpdateTask:1002 withWatcher:p2];
NSArray *watchedTasks = nil;
watchedTasks = [self fetchTasksWatchedByPerson:p1]; // return 2 objects
NSLog(@"watchedTasks : %@\n", watchedTasks);
[self saveContext];
watchedTasks = [self fetchTasksWatchedByPerson:p1]; // return 1 objects
NSLog(@"watchedTasks : %@\n", watchedTasks);
NSArray *allTasks = [self fetchAllTasks]; // return 2 objects
NSLog(@"allTasks : %@\n", watchedTasks);
日志如下。 注意,watchedTasks 的第二个输出只有一个对象!
Printing description of watchedTasks:
<__NSArrayI 0x8d5aff0>(
<Task: 0x8d5b240> (entity: Task; id: 0x8d5b3e0 <x-coredata://706EB8CA-ACE7-48DB-89E1-7CCFCDD41DA4/Task/p2> ; data:
taskId = 1002;
watchers = (
"0x8d57f80 <x-coredata://706EB8CA-ACE7-48DB-89E1-7CCFCDD41DA4/Person/p2>",
"0x8d56070 <x-coredata://706EB8CA-ACE7-48DB-89E1-7CCFCDD41DA4/Person/p1>"
);
),
<Task: 0x8d59d10> (entity: Task; id: 0x8d59420 <x-coredata://706EB8CA-ACE7-48DB-89E1-7CCFCDD41DA4/Task/p1> ; data:
taskId = 1001;
watchers = (
"0x8d57f80 <x-coredata://706EB8CA-ACE7-48DB-89E1-7CCFCDD41DA4/Person/p2>",
"0x8d56070 <x-coredata://706EB8CA-ACE7-48DB-89E1-7CCFCDD41DA4/Person/p1>"
);
)
)
Printing description of updateObjects:
<__NSArrayI 0x8e41880>(
<Task: 0x8d59d10> (entity: Task; id: 0x8d59420 <x-coredata://706EB8CA-ACE7-48DB-89E1-7CCFCDD41DA4/Task/p1> ; data:
taskId = 1001;
watchers = (
"0x8d57f80 <x-coredata://706EB8CA-ACE7-48DB-89E1-7CCFCDD41DA4/Person/p2>",
"0x8d56070 <x-coredata://706EB8CA-ACE7-48DB-89E1-7CCFCDD41DA4/Person/p1>"
);
),
<Task: 0x8d5b240> (entity: Task; id: 0x8d5b3e0 <x-coredata://706EB8CA-ACE7-48DB-89E1-7CCFCDD41DA4/Task/p2> ; data:
taskId = 1002;
watchers = (
"0x8d57f80 <x-coredata://706EB8CA-ACE7-48DB-89E1-7CCFCDD41DA4/Person/p2>",
"0x8d56070 <x-coredata://706EB8CA-ACE7-48DB-89E1-7CCFCDD41DA4/Person/p1>"
);
),
<Person: 0x8d56cc0> (entity: Person; id: 0x8d56070 <x-coredata://706EB8CA-ACE7-48DB-89E1-7CCFCDD41DA4/Person/p1> ; data:
name = aaa;
personId = 111;
),
<Person: 0x8d57be0> (entity: Person; id: 0x8d57f80 <x-coredata://706EB8CA-ACE7-48DB-89E1-7CCFCDD41DA4/Person/p2> ; data:
name = bbb;
personId = 222;
)
)
Printing description of watchedTasks:
<_PFArray 0x8f57da0>(
<Task: 0x8d5b240> (entity: Task; id: 0x8d5b3e0 <x-coredata://706EB8CA-ACE7-48DB-89E1-7CCFCDD41DA4/Task/p2> ; data:
taskId = 1002;
watchers = (
"0x8d57f80 <x-coredata://706EB8CA-ACE7-48DB-89E1-7CCFCDD41DA4/Person/p2>",
"0x8d56070 <x-coredata://706EB8CA-ACE7-48DB-89E1-7CCFCDD41DA4/Person/p1>"
);
)
)
Printing description of allTasks:
<_PFArray 0x8e44b30>(
<Task: 0x8d59d10> (entity: Task; id: 0x8d59420 <x-coredata://706EB8CA-ACE7-48DB-89E1-7CCFCDD41DA4/Task/p1> ; data:
taskId = 1001;
watchers = (
"0x8d57f80 <x-coredata://706EB8CA-ACE7-48DB-89E1-7CCFCDD41DA4/Person/p2>",
"0x8d56070 <x-coredata://706EB8CA-ACE7-48DB-89E1-7CCFCDD41DA4/Person/p1>"
);
),
<Task: 0x8d5b240> (entity: Task; id: 0x8d5b3e0 <x-coredata://706EB8CA-ACE7-48DB-89E1-7CCFCDD41DA4/Task/p2> ; data:
taskId = 1002;
watchers = (
"0x8d57f80 <x-coredata://706EB8CA-ACE7-48DB-89E1-7CCFCDD41DA4/Person/p2>",
"0x8d56070 <x-coredata://706EB8CA-ACE7-48DB-89E1-7CCFCDD41DA4/Person/p1>"
);
)
)
(lldb)
所以我打开 .sqlite 文件看看里面到底有什么。
首先让我感到惊讶的是watchers
在Person
表中,而不是在中间/映射Task2Person
表中。我记得一旦关系存在于中间表中。
那么,Person
表中的watchers
字段只包含一个值,而不是集合或数组!
- (void)viewDidLoad
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
NSArray *watchedTasks = nil;
Person *p1 = [self insertOrUpdatePerson:@"111" withName:@"aaa"];
Person *p2 = [self insertOrUpdatePerson:@"222" withName:@"bbb"];
watchedTasks = [self fetchTasksWatchedByPerson:p1]; // return 1 object
Task *t1 = [self insertOrUpdateTask:1001 withWatcher:p1]; // update watchers in the memory
[self insertOrUpdateTask:1001 withWatcher:p2];
Task *t2 = [self insertOrUpdateTask:1002 withWatcher:p1];
[self insertOrUpdateTask:1002 withWatcher:p2];
watchedTasks = [self fetchTasksWatchedByPerson:p1]; // return 2 objects
[self saveContext];
watchedTasks = [self fetchTasksWatchedByPerson:p1]; // return 1 object
所以我发现在我启动应用程序并从 db 文件中读取任务后,只返回 1 个对象。
当我更新任务的watchers
信息时,可以获取 2 个对象。
最后在我保存上下文之后,又只有 1 个对象了。
忽略这个
- 下面的旧信息
我的代码如下:
NSArray *existedTasks = [[TaskBizDB sharedInstance] fetchTasksWatchedByMeOfProject:projectId];
[context save:&error];
existedTasks = [[TaskBizDB sharedInstance] fetchTasksWatchedByMeOfProject:projectId];
NSArray *allTasks = [[TaskBizDB sharedInstance] fetchTasksOfProject:projectId];
第一行返回两个对象;
第二行保存上下文;
第三行返回只有一个对象,包含在上面的“两个对象”中;
最后一行返回 6 个对象,包含“两个对象”
在第一行返回。
获取接口的工作原理如下:
WXModel *model = [WXModel modelWithEntity:NSStringFromClass([WQPKTeamTask class])];
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"(%@ IN personWatchers) AND (projectId == %d)", currentLoginUser, projectId];
[model setPredicate:predicate];
NSArray *fetchedTasks = [model fetch];
if (fetchedTasks.count == 0) return nil;
return fetchedTasks;
让我感到困惑的是,对于相同的 fetch 请求,为什么在保存后返回不同的结果?
这里有更多细节:
第一行返回的“两个对象”是:
<WQPKTeamTask: 0x1b92fcc0> (entity: WQPKTeamTask; id: 0x1b9300f0 <x-coredata://CFFD3F8B-E613-4DE8-85AA-4D6DD08E88C5/WQPKTeamTask/p9> ; data:
projectId = 372004;
taskId = 338001;
personWatchers = (
"0xf0bf440 <x-coredata://CFFD3F8B-E613-4DE8-85AA-4D6DD08E88C5/WWPerson/p1>"
);
<WQPKTeamTask: 0xf3f6130> (entity: WQPKTeamTask; id: 0xf3cb8d0 <x-coredata://CFFD3F8B-E613-4DE8-85AA-4D6DD08E88C5/WQPKTeamTask/p11> ; data:
projectId = 372004;
taskId = 340006;
personWatchers = (
"0xf0bf440 <x-coredata://CFFD3F8B-E613-4DE8-85AA-4D6DD08E88C5/WWPerson/p1>"
);
第三行返回的唯一一个对象是:
<WQPKTeamTask: 0x1b92fcc0> (entity: WQPKTeamTask; id: 0x1b9300f0 <x-coredata://CFFD3F8B-E613-4DE8-85AA-4D6DD08E88C5/WQPKTeamTask/p9> ; data:
projectId = 372004;
taskId = 338001;
personWatchers = (
"0xf0bf440 <x-coredata://CFFD3F8B-E613-4DE8-85AA-4D6DD08E88C5/WWPerson/p1>"
);
allTasks的打印说明:
<_PFArray 0xf30b9a0>(
<WQPKTeamTask: 0xf3ab9d0> (entity: WQPKTeamTask; id: 0xf3cda40 <x-coredata://CFFD3F8B-E613-4DE8-85AA-4D6DD08E88C5/WQPKTeamTask/p6> ; data: <fault>),
<WQPKTeamTask: 0xf315720> (entity: WQPKTeamTask; id: 0xf3c23a0 <x-coredata://CFFD3F8B-E613-4DE8-85AA-4D6DD08E88C5/WQPKTeamTask/p7> ; data: <fault>),
<WQPKTeamTask: 0xf3a1ed0> (entity: WQPKTeamTask; id: 0xf3cda30 <x-coredata://CFFD3F8B-E613-4DE8-85AA-4D6DD08E88C5/WQPKTeamTask/p8> ; data: <fault>),
<WQPKTeamTask: 0x1b92fcc0> (entity: WQPKTeamTask; id: 0x1b9300f0 <x-coredata://CFFD3F8B-E613-4DE8-85AA-4D6DD08E88C5/WQPKTeamTask/p9> ; data:
projectId = 372004;
taskId = 338001;
personWatchers = (
"0xf0bf440 <x-coredata://CFFD3F8B-E613-4DE8-85AA-4D6DD08E88C5/WWPerson/p1>"
);
),
<WQPKTeamTask: 0xf325e50> (entity: WQPKTeamTask; id: 0xf343820 <x-coredata://CFFD3F8B-E613-4DE8-85AA-4D6DD08E88C5/WQPKTeamTask/p10> ; data: <fault>),
<WQPKTeamTask: 0xf3f6130> (entity: WQPKTeamTask; id: 0xf3cb8d0 <x-coredata://CFFD3F8B-E613-4DE8-85AA-4D6DD08E88C5/WQPKTeamTask/p11> ; data:
projectId = 372004;
taskId = 340006;
personWatchers = (
"0xf0bf440 <x-coredata://CFFD3F8B-E613-4DE8-85AA-4D6DD08E88C5/WWPerson/p1>"
);
)
)
更新 1
如果我调用同一个接口fetchTasksWatchedByMeOfProject:
in:
#pragma mark - NSFetchedResultsController Delegate
- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller
我也会得到“两个对象”。
更新 2
我试过了:
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"(ANY personWatchers == %@) AND (projectId == %d)", currentLoginUser, projectId];
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"(ANY personWatchers.personId == %@) AND (projectId == %d)", currentLoginUserId, projectId];
还是一样的结果。
更新 3
我检查了save:&error
,错误为零。
【问题讨论】:
你看到save:
的返回值是什么,错误是否还是nil? (我不明白这有什么关系,但是 save 有两种方法可以报告问题,而且您都没有检查,至少在此处的代码中。)
@stevesliva 感谢您的提示。我检查了save:
,错误为零。
明确一点,如果返回是YES
,您应该只查看结果然后对错误做出反应。仅查看错误是不够的和/或具有误导性。
【参考方案1】:
保存了什么?
在保存之前打印出-[NSManagedObjectContext updatedObjects]
会发生什么?
最终谓词是什么样的?你能打印出NSFetchRequest
的描述吗?
【讨论】:
感谢提示 :) 我创建了一个新项目来定位问题,并在 .sqlite 文件中发现了一些变化。【参考方案2】:我今天和我的队友一起解决了这个问题。
如果您创建一个没有反向关系的一对多关系,例如,Task
与 watchers
具有一对多关系 Person
,但 Person
与 Task
没有反向关系, 那么关系字段watchers
将被放入Person
表而不是中间/映射表中。
在这种情况下,我在Task
t1 的watchers
中添加一个Person
p1 会将Person
表中的watchers
字段更新为1
。
然后,将Person
p1 也添加到Task
t2 中,watchers
字段将更新为2
,如下所示:
所以,一个Person
只能指向一个Task
!
通过添加从Person
到Task
的反向关系可以绕过这个问题。
【讨论】:
以上是关于CoreData:相同的谓词(IN)在保存操作后返回不同的获取结果的主要内容,如果未能解决你的问题,请参考以下文章
如何在 CoreData iOS 谓词构建器中多次传递相同的参数?