使用 NSPrivateQueueConcurrencyType 和自定义 setter 的核心数据延迟加载不起作用
Core Data Lazy Loading with NSPrivateQueueConcurrencyType and custom setter not working
问题: 当 NSManaged object 时,使用后台线程获取托管 object 不会延迟加载 NSManaged object相关的有一个自定义 setter。在具有主并发类型的主线程上执行提取没有问题。这是为什么?
解决方法: 如果我在关系 object 上创建自定义 getter 并检查是否为零,我可以强制 NSManaged object 通过调用其他没有自定义 setter 方法的变量来加载。
背景
核心数据布局非常简单。我管理了一个游戏 object 和一个管理了回合 object。回合object与游戏object是一对一的关系。我总是获取游戏 object 以访问转弯 object。 TurnImp 和 GameImp 是继承自 Game 和 Turn object 的实现 类,因此我没有将 getter/setter 方法放入自动生成的代码中。
代码
The Fetch
//
//Stick command on background
//
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^ {
//
//Load Game
//
AppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];
CoreDataHelper *coreDataHelper = appDelegate.coreDataHelper;
NSManagedObjectContext *childMOC = [coreDataHelper createChildManagedObjectContext];
//the request
NSFetchRequest *fetchRequest = [NSFetchRequest new];
//the object entity we want
NSEntityDescription *entity = [NSEntityDescription entityForName:GAMEIMP_GAME inManagedObjectContext:childMOC];
[fetchRequest setEntity:entity];
//the predicate rules, the what
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"gameId == %@", @"1404110671234567"];
[fetchRequest setPredicate:predicate];
//the sorting rules
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc]initWithKey:GAMEIMP_OBJECT_ID ascending:YES];
NSArray *sortDescriptors = [[NSArray alloc]initWithObjects:sortDescriptor, nil];
[fetchRequest setSortDescriptors:sortDescriptors];
//Fetch results
NSFetchedResultsController *resultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:childMOC sectionNameKeyPath:nil cacheName:nil];
NSError *error;
BOOL success = [resultsController performFetch:&error];
GameImp *game;
if (success) {
game = [resultsController.fetchedObjects objectAtIndex:0];
} else {
NSLog(@"Unable to get game. Error: %@", error);
}
TurnImp *turnImp = game.turn;
//Issue is here!!! Should be 3, instead 0 because lastRoundReward is nil.
int lastRoundReward = [turnImp.lastRoundReward intValue];
//Work around, call custom getter method. Now 3 is returned.
lastRoundReward = [turnImp getLastRoundReward];
}
本子MOC创作
-(NSManagedObjectContext*) createChildManagedObjectContext {
NSManagedObjectContext *childMOC = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
childMOC.parentContext = self.mainManagedObjectContext;
return childMOC;
}
TurnImp Header
@interface TurnImp : Turn
@property(atomic) BOOL isValid;
- (void) setLastRoundReward: (int) lastRoundReward;
- (int) getLastRoundReward;
@end
TurnImp M
@implementation TurnImp
@synthesize isValid;
@synthesize lastRoundReward = _lastRoundReward;
/**
* Set the last round reward
* @param -
* @return -
*/
- (void) setLastRoundReward: (int) lastRoundReward {
_lastRoundReward = [NSNumber numberWithInt:lastRoundReward];
}
/**
* Get the int value of lastRoundReward
*/
- (int) getLastRoundReward {
//Note - HACK! Lazy loading not working, try another member
if (self.lastRoundReward == nil) {
//Force load
NSString *objectId = self.objectId;
}
return [self.lastRoundReward intValue];
}
将 childMoc 更改为 mainMoc 即可。 MainMoc 代码
//create the main MOC
_mainManagedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
修复并发问题后的更多内容
[childMOC performBlock:^{
// Execute the fetch on the childMOC and do your other work.
NSError *error;
NSArray *results = [childMOC executeFetchRequest:fetchRequest error:&error];
if (results == nil) {
// Handle error
} else if (results.count == 1) {
GameImp *game = [results firstObject];
TurnImp *turnImp = game.turn;
//Issue is here!!! Should be 3, instead 0 because lastRoundReward is nil.
int lastRoundReward = [turnImp.lastRoundReward intValue];
//Work around, call variable objectId (not same as ObjectId)
NSString *objectId = turnImp.objectId;
//not it's 3...
lastRoundReward = [turnImp.lastRoundReward intValue];
}
}];
变通
我从 TurnImp 中删除了以下内容,它按预期方式处理关系。
@synthesize lastRoundReward = _lastRoundReward;
首先,我必须承认我不知道你的问题陈述是什么意思 - 关系的延迟加载到底应该做什么?
但是,快速浏览一下您的代码就会发现您正在使用 NSPrivateQueueConcurrencyType
创建 MOC,但您没有将其使用正确地包装在适当的 performBlock
调用中。
当您明显违反核心数据并发准则时,您就是在危险的水域中玩耍,并且会出现未定义的行为。
此外,为什么要创建 NSFetchedResultsController
的实例只是为了执行提取?那太过分了。只需使用获取请求。像这样...
[childMOC performBlock:^{
// Execute the fetch on the childMOC and do your other work.
NSError *error;
NSArray *results = [childMOC executeFetchRequest:fetchRequest error:&error];
if (result == nil) {
// Handle error
} else if (results.count == 1) {
GameImp *game = [results firstObject];
TurnImp *turnImp = game.turn;
int lastRoundReward = [turn2.lastRoundReward intValue];
}
}];
问题: 当 NSManaged object 时,使用后台线程获取托管 object 不会延迟加载 NSManaged object相关的有一个自定义 setter。在具有主并发类型的主线程上执行提取没有问题。这是为什么?
解决方法: 如果我在关系 object 上创建自定义 getter 并检查是否为零,我可以强制 NSManaged object 通过调用其他没有自定义 setter 方法的变量来加载。
背景 核心数据布局非常简单。我管理了一个游戏 object 和一个管理了回合 object。回合object与游戏object是一对一的关系。我总是获取游戏 object 以访问转弯 object。 TurnImp 和 GameImp 是继承自 Game 和 Turn object 的实现 类,因此我没有将 getter/setter 方法放入自动生成的代码中。
代码
The Fetch
//
//Stick command on background
//
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^ {
//
//Load Game
//
AppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];
CoreDataHelper *coreDataHelper = appDelegate.coreDataHelper;
NSManagedObjectContext *childMOC = [coreDataHelper createChildManagedObjectContext];
//the request
NSFetchRequest *fetchRequest = [NSFetchRequest new];
//the object entity we want
NSEntityDescription *entity = [NSEntityDescription entityForName:GAMEIMP_GAME inManagedObjectContext:childMOC];
[fetchRequest setEntity:entity];
//the predicate rules, the what
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"gameId == %@", @"1404110671234567"];
[fetchRequest setPredicate:predicate];
//the sorting rules
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc]initWithKey:GAMEIMP_OBJECT_ID ascending:YES];
NSArray *sortDescriptors = [[NSArray alloc]initWithObjects:sortDescriptor, nil];
[fetchRequest setSortDescriptors:sortDescriptors];
//Fetch results
NSFetchedResultsController *resultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:childMOC sectionNameKeyPath:nil cacheName:nil];
NSError *error;
BOOL success = [resultsController performFetch:&error];
GameImp *game;
if (success) {
game = [resultsController.fetchedObjects objectAtIndex:0];
} else {
NSLog(@"Unable to get game. Error: %@", error);
}
TurnImp *turnImp = game.turn;
//Issue is here!!! Should be 3, instead 0 because lastRoundReward is nil.
int lastRoundReward = [turnImp.lastRoundReward intValue];
//Work around, call custom getter method. Now 3 is returned.
lastRoundReward = [turnImp getLastRoundReward];
}
本子MOC创作
-(NSManagedObjectContext*) createChildManagedObjectContext {
NSManagedObjectContext *childMOC = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
childMOC.parentContext = self.mainManagedObjectContext;
return childMOC;
}
TurnImp Header
@interface TurnImp : Turn
@property(atomic) BOOL isValid;
- (void) setLastRoundReward: (int) lastRoundReward;
- (int) getLastRoundReward;
@end
TurnImp M
@implementation TurnImp
@synthesize isValid;
@synthesize lastRoundReward = _lastRoundReward;
/**
* Set the last round reward
* @param -
* @return -
*/
- (void) setLastRoundReward: (int) lastRoundReward {
_lastRoundReward = [NSNumber numberWithInt:lastRoundReward];
}
/**
* Get the int value of lastRoundReward
*/
- (int) getLastRoundReward {
//Note - HACK! Lazy loading not working, try another member
if (self.lastRoundReward == nil) {
//Force load
NSString *objectId = self.objectId;
}
return [self.lastRoundReward intValue];
}
将 childMoc 更改为 mainMoc 即可。 MainMoc 代码
//create the main MOC
_mainManagedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
修复并发问题后的更多内容
[childMOC performBlock:^{
// Execute the fetch on the childMOC and do your other work.
NSError *error;
NSArray *results = [childMOC executeFetchRequest:fetchRequest error:&error];
if (results == nil) {
// Handle error
} else if (results.count == 1) {
GameImp *game = [results firstObject];
TurnImp *turnImp = game.turn;
//Issue is here!!! Should be 3, instead 0 because lastRoundReward is nil.
int lastRoundReward = [turnImp.lastRoundReward intValue];
//Work around, call variable objectId (not same as ObjectId)
NSString *objectId = turnImp.objectId;
//not it's 3...
lastRoundReward = [turnImp.lastRoundReward intValue];
}
}];
变通
我从 TurnImp 中删除了以下内容,它按预期方式处理关系。
@synthesize lastRoundReward = _lastRoundReward;
首先,我必须承认我不知道你的问题陈述是什么意思 - 关系的延迟加载到底应该做什么?
但是,快速浏览一下您的代码就会发现您正在使用 NSPrivateQueueConcurrencyType
创建 MOC,但您没有将其使用正确地包装在适当的 performBlock
调用中。
当您明显违反核心数据并发准则时,您就是在危险的水域中玩耍,并且会出现未定义的行为。
此外,为什么要创建 NSFetchedResultsController
的实例只是为了执行提取?那太过分了。只需使用获取请求。像这样...
[childMOC performBlock:^{
// Execute the fetch on the childMOC and do your other work.
NSError *error;
NSArray *results = [childMOC executeFetchRequest:fetchRequest error:&error];
if (result == nil) {
// Handle error
} else if (results.count == 1) {
GameImp *game = [results firstObject];
TurnImp *turnImp = game.turn;
int lastRoundReward = [turn2.lastRoundReward intValue];
}
}];