2015-06-27 85 views
0

我有一个问题,弄清楚如何使用Core Data和MagicalRecord建模/获取项目。我有两个实体,UserMessage,这两者都具有反向关系,对方:MagicalRecord findByAttribute返回反向关系文档问题

用户实体

Relationship Destination Inverse Type 
------------------------------------------------- 
messages  Message  user  To Many 

消息实体

Relationship Destination Inverse Type 
------------------------------------------------- 
user   User   messages To One 

问题

当给定用户获取信息,MagicalRecord正在恢复2X的记录数,因为它是在useruser.messages.user keyPaths匹配属性:

- (NSArray *)fetchMessagesByUser:(NSString *)identifier { 

    // returning 2x the records it should 
    return [Message MR_findByAttribute:@"user.identifier" withValue:identifier]; 
} 

我应该改变我使用MagicalRecord或方式我的核心数据模型有一个基本问题吗?

回答

0

当我使用to many关系设置MagicalRecord的应用程序,然后运行应用程序几次时,用户被一次又一次地保存,所以我看到消息的数量是双倍(和三倍),因为同一个用户有被保存了好几次。

如果您尝试在您的代码中更改用户的属性值 - 以便下次运行程序时创建具有唯一属性的用户 - 我想您会看到消息的数量是你期望什么。

可以通过添加该代码中看到的所有用户:

NSArray* users = [User MR_findAll]; 

for(User* user in users) { 
    NSLog(@"%@", [user name]); //Log whatever attributes you want 
} 

如果我是正确的,这将表明你有相同属性的多个用户。

========

因为对MagicalRecord信息如此之少,我要发布我做了什么设置的应用程序。我使用Xcode 6.3.2。

1)我创建了一个名为MagicalRecord2的OSX Cocoa应用程序。我做了NOT检查使用核心数据。

2)在终端窗口中,我执行的命令:

~$ gem install cocoapods //However, you probably need to use sudo(see below) 

在我的系统,我安装(使用RVM来管理它们)红宝石的多个版本,你永远不应该使用sudo与RVM ,但如果你只有在系统安装Ruby,那么你将需要使用sudo安装宝石:$ sudo gem install cocoapods

3)在终端,我改变了目录(CD)到我的项目目录:

~$ cd ~/xcode_projects/MagicalRecord2/ 

4)在项目目录中,我创建了一个名为Podfile这个 内容文件:

pod "MagicalRecord" 

然后我保存的文件。

5)然后我跑的命令

~/xcode_projects/MagicalRecord2$ pod update 

当该命令完成后,将打印出消息:[!]

请关闭所有当前的Xcode会话和使用 MagicalRecord2.xcworkspace此项目从现在开始。

所以我退出了Xcode,我用Finder导航到我的项目目录,然后点击MagicalRecord2.xcworkspace打开Xcode。如果你在Project Navigator中展开了一些组,你很快会看到正常的AppDelegate.h/.m文件等。

6)在AppDelegate中(或者你可以创建一个你创建的NSWindowController子类),我增加了以下导入:

#import <MagicalRecord/MagicalRecord.h> 

7)然后,我创建的模型:File>New>OSX:CoreData>Data Model

我命名的文件MyModels.xcdatamodeld并接受默认设置。然后在项目导航器中,我点击MyModels.xcdatamodeld文件,打开实体编辑器。在实体编辑器的底部,我点击添加实体。然后我双击“实体”并将“实体”更改为“用户”。在属性下,我点击+,并添加了一个类型为String的name属性。以类似的方式,我添加了一个Message类型为String的'text'属性的实体。

enter image description here

然后我在User实体点击再次,并在Relationships第一节点击了+,并且关系列下我在messages键入(小写,复数),和用于目的地我选择了Message。接下来,在Xcode窗口的最右侧,顶部有一组三个图标,我选择了第三个图标 - Data Model Inspector。随着messages关系强调的,如果你在数据模型中检查一下,你会看到一个写着关系一个标题和在该部分中,您可以看到与TypeTo One值。更改TypeTo Many

enter image description here

接下来,一路过来的实体编辑器的左侧,点击Message实体,并在关系部分,点击“+”,并将其命名为关系“用户”(单数,小写),并且对于选择messages

enter image description here

如果你回到User实体,你会看到,在关系>逆现在说user

数据模型检查器中还有其他设置值得设置,但您必须详细阅读这些设置。对于这个演示,没有其他需要。

8)MyModels.xcdatamodeld仍在项目导航器中突出显示,查看屏幕的顶部,并在Xcode菜单栏中选择Editor>Create NSManagedObject Subclass。在弹出窗口中,我检查了MyModels,然后点击Next,然后我检查了UserMessage,然后再次点击Next。下一个屏幕真的让我感到困惑:Xcode会问你想要保存将要创建的新文件的位置。默认情况下,Xcode将文件添加到项目目录之外,但它们出现在Project Navigator中,因此它们看起来像是在项目中。但后来,当我试图导入这些文件时,我收到一个错误,指出无法找到这些文件。我的回答是,“你是什么意思?!@#〜!@#!@你的意思是?!这些文件就在项目导航器中!“

因此,请确保您仔细选择将文件添加到项目中的组(例如文件夹),例如MagicalRecord2组(文件夹)给我。这有点令人困惑,因为有两个或三个名为“YourProjectName”的东西,当您选择保存新文件的位置时,您必须导航到名为“YourProjectName”的组(文件夹)。不要接受默认位置。

如果您正确操作,您将在“YourProjectName”组(文件夹)中看到新文件User.h/.m和Message.h/.m。下面是它的外观,如果你做它不正确,如:

enter image description here

见顶部的文件怎么不是MagicalRecord2组(文件夹)里面?

9)现在设置MagicalRecord:

// 
// AppDelegate.m 
// MagicalRecord2 
// 


#import "AppDelegate.h" 
#import <MagicalRecord/MagicalRecord.h> 

@interface AppDelegate() 

@property (weak) IBOutlet NSWindow *window; 
@end 

@implementation AppDelegate 

- (void)applicationDidFinishLaunching:(NSNotification *)aNotification { 
    // Insert code here to initialize your application 

    [MagicalRecord setupCoreDataStackWithStoreNamed:@"MyDatabase.sqlite"]; 
} 

- (void)applicationWillTerminate:(NSNotification *)aNotification { 
    // Insert code here to tear down your application 

    [MagicalRecord cleanUp]; 
} 

@end 

正如你所看到的,你只需要两行设置神奇的记录,这反过来将带你建立核心数据的照顾。

10)接下来,编写,您可以调用创建并保存新的用户的便捷方法:

#import "AppDelegate.h" 
#import <MagicalRecord/MagicalRecord.h> 
#import "User.h" 
#import "Message.h" 

@interface AppDelegate() 
... 
... 

- (void)persistNewUserWithName:(NSString*) name { 

    NSManagedObjectContext* defaultContext = 
    [NSManagedObjectContext MR_defaultContext]; //[NSManagedObjectContext MR_contextForCurrentThread] => deprecated 

    User* myUser = [User MR_createEntityInContext:defaultContext]; //Requires that you import User.h. 
    [myUser setName: name]; 

    Message* msg1 = [Message MR_createEntityInContext:defaultContext]; //Requires that you import Message.h 
    [msg1 setText:@"Hello"]; 
    [msg1 setUser:myUser]; 

    Message* msg2 = [Message MR_createEntityInContext:defaultContext]; 
    [msg2 setText:@"Goodbye"]; 
    [msg2 setUser:myUser]; 

    //I commented out the following line so that the database starts out 
    //empty every time I run the project: 

    //[defaultContext MR_saveToPersistentStoreAndWait]; //[defaultContext MR_save] => deprecated 

} 

@end 

11)使用MagicalRecord查询核心数据:

@implementation AppDelegate 

- (void)applicationDidFinishLaunching:(NSNotification *)aNotification { 
    // Insert code here to initialize your application 

    [MagicalRecord setupCoreDataStackWithStoreNamed:@"MyDatabase.sqlite"]; 


    [self persistNewUserWithName:@"Rokkor"]; 
    NSArray* users = [User MR_findAll]; 
    NSLog(@"User count: %lu", (unsigned long)[users count]); 

    for(User* user in users) { 
     NSLog(@"%@", [user name]); 
    } 

    NSArray* matchingMessages = 
    [Message MR_findByAttribute:@"user.name" withValue:@"Rokkor"]; 
    //When you search in the Message entity, as above, you can write "user.name", 
    //but if you search in the User entity, you cannot write "messages" 

    for (Message* msg in matchingMessages) { 
     NSLog(@"%@", [msg text]); 
    } 



} 

我得到了一个错误说无法找到文件NSManagedObject.h。在用户中。米,有线:

#import NSManagedObject.h 

我注释掉了这条线,似乎工作。 Xcode创建了这个文件,我不确定为什么它为一个不存在的文件创建了一个带有导入语句的文件。

这里是控制台输出:

2015-06-28 19:42:24.845 MagicalRecord2[2924:73871] Created new private queue context: <NSManagedObjectContext: 0x6000001e7100> 
2015-06-28 19:42:24.845 MagicalRecord2[2924:73871] Set root saving context: <NSManagedObjectContext: 0x6000001e7100> 
2015-06-28 19:42:24.845 MagicalRecord2[2924:73871] Created new main queue context: <NSManagedObjectContext: 0x6080001e4900> 
2015-06-28 19:42:24.846 MagicalRecord2[2924:73871] Set default context: <NSManagedObjectContext: 0x6080001e4900> 
2015-06-28 19:42:24.847 MagicalRecord2[2924:73871] User count: 1 
2015-06-28 19:42:24.847 MagicalRecord2[2924:73871] Rokkor 
2015-06-28 19:42:24.848 MagicalRecord2[2924:73871] Hello 
2015-06-28 19:42:24.848 MagicalRecord2[2924:73871] Goodbye 

这里有一对夫妇的其他选项:

1)您可以使用块来获得的DefaultContext并保存数据:

[MagicalRecord saveWithBlock:^(NSManagedObjectContext* defaultContext) { 

    User* myUser = [User MR_createEntityInContext:defaultContext]; 
    [myUser setName: name]; 


    Message* m1 = [Message MR_createEntityInContext:defaultContext]; 
    [m1 setText:@"Hello"]; 
    [m1 setUser:myUser]; 

    Message* m2 = [Message MR_createEntityInContext:defaultContext]; 
    [m2 setText:@"Goodbye"]; 
    [m2 setUser:myUser]; 

}]; 

据推测,saveWithBlock()定义如下:

(void)saveWithBlock:(void(^)(NSManagedObjectContext*))yourBlock { 

    NSManagedObjectContext* context = 
     [NSManagedObjectContext MR_defaultContext]; 

    yourBlock(context) 

    [context MR_saveToPersistentStoreAndWait]; 
} 

因此,saveWithBlock()将自动保存添加到上下文的任何内容。

2)查询数据时,您可以使用过滤器:

NSPredicate* messagesFilter = 
[NSPredicate predicateWithFormat:@"user.name == %@ && text == %@", 
@"Rokkor", @"Hello"]; 

NSArray* matchingMessages = [Message MR_findAllWithPredicate:messagesFilter]; 

for (Message* msg in matchingMessages) { 
    NSLog(@"%@", [msg text]); 
} 
+0

这是一个非常完整的答案。我肯定有几件事情会发现它很有用。谢谢。 – rokkor