在核心数据中,我有一个通过桥接实体连接的关系
有问题的关系是卡 - >>细分 - >>战斗机
card
有许多segments
segment
有多个fighters
单个fighter
可以显示segment
一个段(或SegmentRow)附加了一系列战士。
它的图表如下所示;
当前应用程序的工作方式是,用户选择要添加到细分行的战斗机。一旦他们点击保存,分配给该行的段行和战斗机就会被保存。
但我一直得到关系错误;
非法企图建立关系'战士'对象之间 在不同的上下文中(来源=段 - 名称:0行与0战士 ,目的地=一些战士)'
一些注意事项:
卡片对象尚不存在,这是因为需要先保存片段;然后可以保存卡片对象。
所以我保存的代码是这样的;
+ (void) saveSegments:(NSArray *)segments inContext:(NSManagedObjectContext *)context withCompletion:(void (^)(BOOL success, NSError *error))completion
{
int i = 0;
for (FCSegmentRow *row in segments) {
FCSegment *segment = [FCSegment MR_createEntityInContext:context];
segment.name = [NSString stringWithFormat:@"Row %lu", (unsigned long)i];
// Fighters
NSMutableSet *fighters = [NSMutableSet set];
for (FCFighter *fighter in row.fighters) {
if (fighter) {
[fighters addObject:fighter];
}
}
segment.fighters = [NSSet setWithSet:fighters];
i++;
}
[context MR_saveToPersistentStoreAndWait];
// Output
NSArray *allSegments = [self segmentsInContext:context error:nil];
for (FCSegment *segment in allSegments) {
NSLog(@"%@", [segment description]);
}
}
FCSegmentRow只是一个带有NSArray *战斗机的简单NSObject;
在我的代码中,我尝试设置一个可变集并向此集添加战斗机,然后将其保存到核心数据。
此外,我试图将保存放在一个块中等待完成;但是这个问题还在继续发生。
这似乎是因为战斗机与我正在创建的细分市场处于不同的背景中。
所以,我的问题是 - 我如何将其与战士存储(关系)
编辑:战士在哪里以及如何创建?
参考问题
战士们在他们自己的页面上(视图控制器),并有自己的托管对象上下文。
管理细分的页面允许用户从战士列表中选择;这将启动战斗机页面并等待完成程序段返回"返回"选定的战斗机。
战斗机数据来自Firebase,并将其加载到Core数据中。
因此配置了战斗机视图控制器;
- (void)viewDidLoad
{
[super viewDidLoad];
self.title = @"Pick Fighter";
self.tableView.delegate = self;
self.tableView.dataSource = self;
self.context = [NSManagedObjectContext MR_context];
[self reloadData];
}
// ...
-(void) reloadData
{
self.fighterList = [FCFighter findFightersInContext:self.context];
[self.tableView reloadData];
}
如上所示,战士视图控制器有自己的上下文
编辑: 补充说明:
一张卡由许多段组成,我将其称为段行。
在"段行视图控制器"每个表格单元格中有2个按钮(一个区段有2个战斗机),用户可以按任一按钮激活战斗机页面,让用户选择所需的战斗机。
我尝试将段行视图控制器中的相同上下文传递到fighters页面,但是当我按下第二个按钮时,它会从第一个按钮丢失数据。
IE: 按下按钮A - 选择一个战士..乔博格斯 按下按钮B - 挑选一名战士..迈克史密斯 当我回到段行;按钮A的战斗机消失了;当我检查数据时(null)。
启动战斗机页面的代码如下;
UIStoryboard *mainStoryboard = [UIStoryboard mainStoryboard];
FCFightersTableViewController *vc = (FCFightersTableViewController *) [mainStoryboard instantiateViewControllerWithIdentifier:@"FCFightersTableViewController"];
vc.tableCellSelectable = YES;
vc.excludeFighters = [selectedFightersMutable copy];
//vc.context = self.context;
vc.completionBlock = ^(FCFightersTableViewController *vc, FCFighter *fighter) {
NSLog(@"Picked fighter - %@ (uuid: %@)", fighter.name, fighter.uuid);
[fightersInRow setObject:fighter atIndexedSubscript:fighterIdx];
segmentRow.fighters = [fightersInRow copy];
NSLog(@"selected fighter - %@ (uuid: %@)", fighter.name, fighter.uuid);
[fightersInRow setObject:fighter atIndexedSubscript:fighterIdx];
segmentRow.fighters = [fightersInRow copy];
[self.tableView beginUpdates];
[self.tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationNone];
[self.tableView endUpdates];
};
答案 0 :(得分:0)
只是想知道为什么要使用多个上下文?它的管理方式更复杂,我只是在使用多线程时发现它是必要的 - 例如,当实例化可能使主线程缓慢的UI的大量对象时。 Coredata大多可以用一个上下文完成它的东西,你不会注意到任何缓慢。管理多个上下文需要大量的计划,在您的情况下可能完全没有必要(但如果您别无选择,请道歉)。
另一件事 - 你不需要创建NSSets然后将它们添加到其他对象。 Coredata涵盖了所有内容(特别是如果您从coredata自动创建对象)。 NSSets将随时可用,你只需要在主上下文中创建对象并设置它们的关系属性 - 父母等。
答案 1 :(得分:0)
我接受@ pbasdf的回答。
这就是我的所作所为。我没有使用全局上下文,而是保留它,以便每个视图控制器都有自己的NSManagedObjectContext。
当用户在SegmentRow(页面A)上时,用户可以按下按钮以启动战斗机列表(页面B);返回完成块;
// (Page A) fires Page B
UIStoryboard *mainStoryboard = [UIStoryboard mainStoryboard];
FCFightersTableViewController *vc = (FCFightersTableViewController *) [mainStoryboard instantiateViewControllerWithIdentifier:@"FCFightersTableViewController"];
vc.completionBlock = ^(FCFightersTableViewController *vc, NSManagedObjectID *fighterObjectID) {
FCFighter *fighter = [self.context objectWithID:fighterObjectID];
NSLog(@"Picked fighter - %@ (uuid: %@)", fighter.name, fighter.uuid);
[fightersInRow replaceObjectAtIndex:fighterIdx withObject:fighter];
segmentRow.fighters = [fightersInRow copy];
[self.tableView beginUpdates];
[self.tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationNone];
[self.tableView endUpdates];
};
请注意,我已根据上述评论将其从setObject
更改为replaceObject
。
根据pbasdf的回答,我也得到ManagedObjectID
并使用上下文来获得战斗机。
// (Page B) fighters page fires the completionBlock
-(void) tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[tableView deselectRowAtIndexPath:indexPath animated:NO];
FCFighter *fighter = nil;
fighter = [self.fighterList objectAtIndex:indexPath.row];
if (self.completionBlock)
{
self.completionBlock(self, fighter.objectID);
}
[self.navigationController dismissViewControllerAnimated:YES completion:^{
[MBProgressHUD hideHUDForView:self.view animated:YES];
}];
}
当我来保存它时,我现在;
// Save the segments to Core data
for (FCSegmentRow *row in segments) {
FCSegment *segment = [FCSegment MR_createEntityInContext:context];
segment.name = [NSString stringWithFormat:@"Row %lu", (unsigned long)i];
[segment addFighters:[NSSet setWithArray:row.fighters]];
}
[context MR_saveToPersistentStoreAndWait];
// Output log
NSArray *allSegments = [self segmentsInContext:context error:nil];
for (FCSegment *segment in allSegments) {
NSLog(@"%@", [segment description]);
for (FCFighter *fighter in [segment.fighters allObjects]) {
NSLog(@"Fighter -- %@", fighter.name);
}
}
虽然我收到核心数据错误,但我会将此问题放在另一个问题中,当我点击输出日志时,它会显示给定段的战斗机。
我必须做一些检查以确定它是否持久并正确取出物体;但除此之外,我接受@ pbasdf的回答。
非常感谢