问题 核心数据关系:如何将新对象插入实体并创建与另一实体中现有对象的关系


我正在构建一个小型iPhone应用程序,允许用户记录他们可能正在与朋友一起玩的游戏。我现在需要在Core Data中使用关系,但似乎无法让它工作。

我希望能够在创建与不同实体中的现有数据的关系的同时将新数据添加到一个实体中。我怎样才能做到这一点?

请注意我是Core Data的新手,并且今天花了很多时间试图解决这个问题,但运气不佳。任何帮助将非常感谢。


我有3个实体: 比分游戏 和 玩家

比分 属性: dateplayer1Scoreplayer2Score 和 status

游戏 属性: title

玩家 属性: name

我之间有很多很多关系(比分 << --- >> 游戏)和(比分 << --- >> 玩家


我已经有了一个游戏和玩家的列表。用户选择正在播放的游戏和谁,并且在该信息中创建一组对象 比分 与所选游戏和玩家有关系的实体。

这是我的来源:

//  Scores.h

#import <Foundation/Foundation.h>
#import <CoreData/CoreData.h>

@class Games, Players;

@interface Scores : NSManagedObject

@property (nonatomic, retain) NSDate * date;
@property (nonatomic, retain) NSNumber * player1Score;
@property (nonatomic, retain) NSNumber * player2Score;
@property (nonatomic, retain) NSNumber * status;
@property (nonatomic, retain) NSSet *game;
@property (nonatomic, retain) NSSet *player;
@end

@interface Scores (CoreDataGeneratedAccessors)

- (void)addGameObject:(Games *)value;
- (void)removeGameObject:(Games *)value;
- (void)addGame:(NSSet *)values;
- (void)removeGame:(NSSet *)values;

- (void)addPlayerObject:(Players *)value;
- (void)removePlayerObject:(Players *)value;
- (void)addPlayer:(NSSet *)values;
- (void)removePlayer:(NSSet *)values;

@end

// SC_ScoreViewController.h

#import <UIKit/UIKit.h>

@interface SC_ScoreViewController : UIViewController

@property (strong) NSIndexPath *game;
@property (strong) NSIndexPath *playerOne;
@property (strong) NSIndexPath *playerTwo;

@property (weak, nonatomic) IBOutlet UILabel *playerOneName;
@property (weak, nonatomic) IBOutlet UILabel *playerTwoName;

@end

//  SC_ScoreViewController.m

#import "SC_ScoreViewController.h"
#import "Scores.h"
#import "Players.h"
#import "Games.h"

@interface SC_ScoreViewController ()

@end

@implementation SC_ScoreViewController

@synthesize game;
@synthesize playerOne;
@synthesize playerTwo;

// managedObjectContext (context)
- (NSManagedObjectContext *)managedObjectContext {
    NSManagedObjectContext *context = nil;
    id delegate = [[UIApplication sharedApplication] delegate];
if ([delegate respondsToSelector:@selector(managedObjectContext)]) {
        context = [delegate managedObjectContext];
    }
    return context;
}

- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
    self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
    if (self) {
        // Custom initialization
    }
    return self;
}

- (void)viewDidLoad
{
    [super viewDidLoad];

    // Setup Nav Title
    self.navigationItem.title = [self.game valueForKey:@"title"];

    // Setup Player's Names
    [self.playerOneName setText:[self.playerOne valueForKey:@"name"]];
    [self.playerTwoName setText:[self.playerTwo valueForKey:@"name"]];


    Scores * newEntry = [NSEntityDescription insertNewObjectForEntityForName:@"Scores"        inManagedObjectContext:self.managedObjectContext];
    newEntry.player1Score = 0;
    newEntry.player2Score = 0;
    newEntry.status = nil;
    newEntry.player = self.playerOne; // Incompatible pointer types assigning to NSSet from NSIndexPath

    NSError *error;
    if (![self.managedObjectContext save:&error]) {
        NSLog(@"Whoops, couldn't save: %@", [error localizedDescription]);
    }

    - (void)didReceiveMemoryWarning
    {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
    }

    @end

我希望我已经清楚并提供了足够的信息。这是我的第一个问题,所以我希望一切顺利。任何帮助都会很棒,谢谢。


6242
2017-10-10 18:07


起源



答案:


你必须插入 Players'对象进入 self.managedObjectContext 与插入新内容完全相同 Score 目的。它可能是这样的:

Scores *score = [NSEntityDescription insertNewObjectForEntityForName:@"Scores" inManagedObjectContext:self.managedObjectContext];
Player *player = [NSEntityDescription insertNewObjectForEntityForName:@"Player" inManagedObjectContext:self.managedObjectContext];
[score addPlayerObject:player];

你还必须改变你的关系“得分<< --- >>玩家”,因为现在你不知道哪个玩家是哪个。这里的其他选择是使用两个多对一关系(分别用于player1和player2)。

还有一件事:以单数形式命名实体是最佳实践,所以 Score, 代替 Scores 等等。

你应该读 创建托管对象关系。在那里很好地描述了它们。

更新: 我认为游戏和分数之间不应该存在多对多的关系(据我所知,分数可以重命名为匹配)。它可能应该是一对多的关系。


8
2017-10-10 18:17



嗨,谢谢你这么快回答。如果你不介意,我会有几个跟进问题。看看你的代码,一个新的玩家将与分数一起创建,对吗?如果是这样,我的问题是我已经有玩家并希望将它们与得分相关联,其他明智的我只是重新创建它们。此外,我有很多关系,因为分数可以有多个游戏,游戏可以有多个分数,与玩家和分数相同。 - Dean Ridgeley
抱歉,我完全不了解您的数据模型(您的关系)。至于球员,我看到你有 self.playerOne 和 self.playerOneName。它们分别是以下类型: NSIndexPath 和 UILabel。当您使用核心数据关系时,您必须使用 NSManagedObject的。我假设你还没有它们(因为它们在你的视图控制器中不存在),这就是为什么我创建了一个带有行的新玩家:'Player * player = [NSEntityDescription(...)'。 - Arek Holko
我已经尝试过使用NSManagedObject但无法正常工作。我不断收到错误“从NSManageObject分配给NSSet的指针类型不兼容”。我觉得我在这里遗漏了一些东西,我需要用什么代码来创建从分数到玩家的关系?我应该为每个玩家使用两个独立的关系吗? - Dean Ridgeley
你得到这个错误,因为 addPlayer: 预计 NSSet,但你试着分配给它 NSManagedObject。 addPlayerObject: 期待单身 NSManagedObject。正如我在答案中写的那样,你可以改变你的关系来订购(然后你会得到 @property (nonatomic, retain) NSOrderedSet *players;)或使用两个多对一关系。 - Arek Holko


答案:


你必须插入 Players'对象进入 self.managedObjectContext 与插入新内容完全相同 Score 目的。它可能是这样的:

Scores *score = [NSEntityDescription insertNewObjectForEntityForName:@"Scores" inManagedObjectContext:self.managedObjectContext];
Player *player = [NSEntityDescription insertNewObjectForEntityForName:@"Player" inManagedObjectContext:self.managedObjectContext];
[score addPlayerObject:player];

你还必须改变你的关系“得分<< --- >>玩家”,因为现在你不知道哪个玩家是哪个。这里的其他选择是使用两个多对一关系(分别用于player1和player2)。

还有一件事:以单数形式命名实体是最佳实践,所以 Score, 代替 Scores 等等。

你应该读 创建托管对象关系。在那里很好地描述了它们。

更新: 我认为游戏和分数之间不应该存在多对多的关系(据我所知,分数可以重命名为匹配)。它可能应该是一对多的关系。


8
2017-10-10 18:17



嗨,谢谢你这么快回答。如果你不介意,我会有几个跟进问题。看看你的代码,一个新的玩家将与分数一起创建,对吗?如果是这样,我的问题是我已经有玩家并希望将它们与得分相关联,其他明智的我只是重新创建它们。此外,我有很多关系,因为分数可以有多个游戏,游戏可以有多个分数,与玩家和分数相同。 - Dean Ridgeley
抱歉,我完全不了解您的数据模型(您的关系)。至于球员,我看到你有 self.playerOne 和 self.playerOneName。它们分别是以下类型: NSIndexPath 和 UILabel。当您使用核心数据关系时,您必须使用 NSManagedObject的。我假设你还没有它们(因为它们在你的视图控制器中不存在),这就是为什么我创建了一个带有行的新玩家:'Player * player = [NSEntityDescription(...)'。 - Arek Holko
我已经尝试过使用NSManagedObject但无法正常工作。我不断收到错误“从NSManageObject分配给NSSet的指针类型不兼容”。我觉得我在这里遗漏了一些东西,我需要用什么代码来创建从分数到玩家的关系?我应该为每个玩家使用两个独立的关系吗? - Dean Ridgeley
你得到这个错误,因为 addPlayer: 预计 NSSet,但你试着分配给它 NSManagedObject。 addPlayerObject: 期待单身 NSManagedObject。正如我在答案中写的那样,你可以改变你的关系来订购(然后你会得到 @property (nonatomic, retain) NSOrderedSet *players;)或使用两个多对一关系。 - Arek Holko


您可以考虑重新设计对象图。从现实来看,我脑子里有一个关于游戏,玩家和分数如何相互关联的概念。但是,您的对象图似乎与该概念不匹配(但您对游戏,玩家和分数之间的相互关系的概念可能与我的不同)。

我将消除所有这些多对多关系,并用一对多关系替换它们。例如,玩家可以有很多分数,但分数不会有很多玩家。三名球员可能得分为100,但这并不意味着他们共享一个得分对象(值为100)。

这是我如何设计对象图:

游戏实体:

  • 标题属性(例如,“垄断”)
  • 球员关系,一对多(一场比赛,很多球员)

玩家实体:

  • name属性(例如,Joe Smith)
  • 游戏关系,一对多(一个玩家,许多游戏)
  • 得分关系,一对多(一个球员,多个得分)

分数实体:

  • 得分属性,int值(例如,100)
  • 游戏关系,一对一(一个分数,一个游戏)

2
2017-10-10 20:09





在设计模型时考虑它的另一种方法如下:

游戏 - 是代表游戏类型的对象(垄断,网球等)

匹配 - 实际的“游戏”,发生在某个时间点,并具有日期/时间或期间等属性。加上player1,player2等......无论谁真正参加。加上每个团队或玩家得分,例如player1Score,player2Score ......

竞争对手 - 或者玩家。


0
2017-10-12 12:24