核心数据 - 使用数组进行保存,导致'非法尝试建立关系'

时间:2016-02-20 11:02:35

标签: objective-c core-data nsmanagedobjectcontext

在核心数据中,我有一个通过桥接实体连接的关系

有问题的关系是卡 - >>细分 - >>战斗机

  • card有许多segments
  • segment有多个fighters单个
  • fighter可以显示segment

一个段(或SegmentRow)附加了一系列战士。

它的图表如下所示;

Entity diagram

当前应用程序的工作方式是,用户选择要添加到细分行的战斗机。一旦他们点击保存,分配给该行的段行和战斗机就会被保存。

但我一直得到关系错误;

  

非法企图建立关系'战士'对象之间   在不同的上下文中(来源=段 - 名称: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;

在我的代码中,我尝试设置一个可变集并向此集添加战斗机,然后将其保存到核心数据。

此外,我试图将保存放在一个块中等待完成;但是这个问题还在继续发生。

这似乎是因为战斗机与我正在创建的细分市场处于不同的背景中。

所以,我的问题是 - 我如何将其与战士存储(关系)

编辑:战士在哪里以及如何创建?

参考问题

  1. 战士们在他们自己的页面上(视图控制器),并有自己的托管对象上下文。

  2. 管理细分的页面允许用户从战士列表中选择;这将启动战斗机页面并等待完成程序段返回"返回"选定的战斗机。

  3. 战斗机数据来自Firebase,并将其加载到Core数据中。

  4. 因此配置了战斗机视图控制器;

    - (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];
        };
    

2 个答案:

答案 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的回答。

非常感谢