MagicalRecord findByAttribute 返回反向关系文档问题
MagicalRecord findByAttribute returning inverse relationship documents issue
我在弄清楚如何 model/fetch 带有 Core Data 和 MagicalRecord 的项目时遇到了问题。我有两个实体,User
和 Message
,它们彼此之间存在反向关系:
用户实体
Relationship Destination Inverse Type
-------------------------------------------------
messages Message user To Many
消息实体
Relationship Destination Inverse Type
-------------------------------------------------
user User messages To One
问题
当获取给定用户的消息时,MagicalRecord 返回 2 倍的记录数,因为它在 user
和 user.messages.user
keyPaths:
匹配属性
- (NSArray *)fetchMessagesByUser:(NSString *)identifier {
// returning 2x the records it should
return [Message MR_findByAttribute:@"user.identifier" withValue:identifier];
}
我应该改变我使用 MagicalRecord 的方式还是我的 Core Data 模型存在基本问题?
当我使用 to many
关系设置一个带有 MagicalRecord 的应用程序,然后 运行 几次应用程序时,用户一次又一次地被保存,所以我看到两倍(和三倍)消息数,因为同一个用户已保存多次。
如果您尝试在代码中更改用户的属性值——以便下次您 运行 您的程序时创建具有独特属性的用户——我想您会看到消息数量将如您所愿。
您可以通过添加此代码查看所有用户:
NSArray* users = [User MR_findAll];
for(User* user in users) {
NSLog(@"%@", [user name]); //Log whatever attributes you want
}
如果我是对的,那将表明您有多个具有相同属性的用户。
========
因为关于 MagicalRecord 的信息太少了,我将post 设置应用程序。我用的是 Xcode 6.3.2。
1) 我创建了一个 OSX Cocoa 应用程序,我将其命名为 MagicalRecord2
。我做了 NOT
检查使用核心数据。
2) 在终端 window 中,我执行了命令:
~$ gem install cocoapods //However, you probably need to use sudo(see below)
在我的系统上,我安装了多个版本的 ruby(使用 rvm 来管理它们),你不应该将 sudo
与 rvm 一起使用,但是如果你只安装了系统ruby,那么你将需要使用sudo
来安装gem:$ sudo gem install cocoapods
3) 在终端中,我将目录 (cd) 更改为我的项目目录:
~$ cd ~/xcode_projects/MagicalRecord2/
4) 在项目目录中,我用这个创建了一个名为 Podfile
的文件
内容:
pod "MagicalRecord"
然后我保存了文件。
5) 然后我运行命令
~/xcode_projects/MagicalRecord2$ pod update
该命令完成后,打印出消息:
[!] Please close any current Xcode sessions and use
MagicalRecord2.xcworkspace
for this project from now on.
所以我退出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
文件,这会打开实体编辑器。在实体编辑器的底部,我单击了添加实体。然后我双击“实体”并将 "Entity" 更改为“用户”。在“属性”下,我单击 +
,然后添加了一个字符串类型的 name
属性。以类似的方式,我添加了一个带有字符串类型的“文本”属性的消息实体。
然后我再次点击 User
实体,在 Relationships
部分我点击了 +
,在 Relationship 列我输入了 messages
(小写,复数),对于 Destination 我选择了 Message
。接下来,在 Xcode window 的最右侧,顶部有一组三个图标,我 select 编辑了第三个图标 - Data Model Inspector
.突出显示 messages
关系后,如果您在数据模型检查器中查看,您将看到一个标题为 Relationship 并且在该部分中您可以看到 Type
和To One
的值。将 Type
更改为 To Many
:
接下来,在实体编辑器的左侧,单击 Message
实体,然后在 Relationships 部分单击“+”,然后将关系命名为“用户”(单数,小写),并为 Inverse select messages
。
如果您返回 User
实体,您会看到 Relationships>Inverse 下现在显示 user
。
数据模型检查器中还有其他值得设置的设置,但您必须仔细阅读这些设置。对于此演示,不需要其他任何东西。
8) MyModels.xcdatamodeld
在项目导航器中仍然突出显示,抬头查看屏幕顶部,然后在 Xcode 菜单栏 select Editor>Create NSManagedObject Subclass
中。在弹出窗口 window 中,我选中 MyModels
,然后单击下一步,然后选中 User
和 Message
,然后再次单击下一步。下一个屏幕真的把我搞砸了:Xcode 询问您要将要创建的新文件保存到哪里。默认情况下 Xcode 将文件添加到项目目录之外,但它们会出现在项目导航器中,因此它们看起来就像在项目中一样。但后来,当我尝试导入这些文件时,出现错误,提示找不到这些文件。我的回答是,“什么 ~!@#~!@#!@ 你是什么意思??!这些文件就在项目导航器中!!”
因此,请务必仔细选择将文件添加到项目中的组(即文件夹),例如MagicalRecord2 组(文件夹)对我来说。这有点令人困惑,因为有两三个名为“YourProjectName”的东西,当您选择保存新文件的位置时,您必须导航到名为“YourProjectName”的组(文件夹)。不要接受默认的 LO阳离子。
如果操作正确,您将在“YourProjectName”组(文件夹)中看到新文件 User.h/.m 和 Message.h/.m。如果你操作不当,它会是这样的:
看看顶部的文件怎么不在 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
如您所见,您只需要两行代码即可设置 Magical Record,它会依次为您设置 Core Data。
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查询Core Data:
@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
。在 User.m 中,有一行:
#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)可以使用block获取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]);
}
我在弄清楚如何 model/fetch 带有 Core Data 和 MagicalRecord 的项目时遇到了问题。我有两个实体,User
和 Message
,它们彼此之间存在反向关系:
用户实体
Relationship Destination Inverse Type
-------------------------------------------------
messages Message user To Many
消息实体
Relationship Destination Inverse Type
-------------------------------------------------
user User messages To One
问题
当获取给定用户的消息时,MagicalRecord 返回 2 倍的记录数,因为它在 user
和 user.messages.user
keyPaths:
- (NSArray *)fetchMessagesByUser:(NSString *)identifier {
// returning 2x the records it should
return [Message MR_findByAttribute:@"user.identifier" withValue:identifier];
}
我应该改变我使用 MagicalRecord 的方式还是我的 Core Data 模型存在基本问题?
当我使用 to many
关系设置一个带有 MagicalRecord 的应用程序,然后 运行 几次应用程序时,用户一次又一次地被保存,所以我看到两倍(和三倍)消息数,因为同一个用户已保存多次。
如果您尝试在代码中更改用户的属性值——以便下次您 运行 您的程序时创建具有独特属性的用户——我想您会看到消息数量将如您所愿。
您可以通过添加此代码查看所有用户:
NSArray* users = [User MR_findAll];
for(User* user in users) {
NSLog(@"%@", [user name]); //Log whatever attributes you want
}
如果我是对的,那将表明您有多个具有相同属性的用户。
========
因为关于 MagicalRecord 的信息太少了,我将post 设置应用程序。我用的是 Xcode 6.3.2。
1) 我创建了一个 OSX Cocoa 应用程序,我将其命名为 MagicalRecord2
。我做了 NOT
检查使用核心数据。
2) 在终端 window 中,我执行了命令:
~$ gem install cocoapods //However, you probably need to use sudo(see below)
在我的系统上,我安装了多个版本的 ruby(使用 rvm 来管理它们),你不应该将 sudo
与 rvm 一起使用,但是如果你只安装了系统ruby,那么你将需要使用sudo
来安装gem:$ sudo gem install cocoapods
3) 在终端中,我将目录 (cd) 更改为我的项目目录:
~$ cd ~/xcode_projects/MagicalRecord2/
4) 在项目目录中,我用这个创建了一个名为 Podfile
的文件
内容:
pod "MagicalRecord"
然后我保存了文件。
5) 然后我运行命令
~/xcode_projects/MagicalRecord2$ pod update
该命令完成后,打印出消息:
[!] Please close any current Xcode sessions and use
MagicalRecord2.xcworkspace
for this project from now on.
所以我退出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
文件,这会打开实体编辑器。在实体编辑器的底部,我单击了添加实体。然后我双击“实体”并将 "Entity" 更改为“用户”。在“属性”下,我单击 +
,然后添加了一个字符串类型的 name
属性。以类似的方式,我添加了一个带有字符串类型的“文本”属性的消息实体。
然后我再次点击 User
实体,在 Relationships
部分我点击了 +
,在 Relationship 列我输入了 messages
(小写,复数),对于 Destination 我选择了 Message
。接下来,在 Xcode window 的最右侧,顶部有一组三个图标,我 select 编辑了第三个图标 - Data Model Inspector
.突出显示 messages
关系后,如果您在数据模型检查器中查看,您将看到一个标题为 Relationship 并且在该部分中您可以看到 Type
和To One
的值。将 Type
更改为 To Many
:
接下来,在实体编辑器的左侧,单击 Message
实体,然后在 Relationships 部分单击“+”,然后将关系命名为“用户”(单数,小写),并为 Inverse select messages
。
如果您返回 User
实体,您会看到 Relationships>Inverse 下现在显示 user
。
数据模型检查器中还有其他值得设置的设置,但您必须仔细阅读这些设置。对于此演示,不需要其他任何东西。
8) MyModels.xcdatamodeld
在项目导航器中仍然突出显示,抬头查看屏幕顶部,然后在 Xcode 菜单栏 select Editor>Create NSManagedObject Subclass
中。在弹出窗口 window 中,我选中 MyModels
,然后单击下一步,然后选中 User
和 Message
,然后再次单击下一步。下一个屏幕真的把我搞砸了:Xcode 询问您要将要创建的新文件保存到哪里。默认情况下 Xcode 将文件添加到项目目录之外,但它们会出现在项目导航器中,因此它们看起来就像在项目中一样。但后来,当我尝试导入这些文件时,出现错误,提示找不到这些文件。我的回答是,“什么 ~!@#~!@#!@ 你是什么意思??!这些文件就在项目导航器中!!”
因此,请务必仔细选择将文件添加到项目中的组(即文件夹),例如MagicalRecord2 组(文件夹)对我来说。这有点令人困惑,因为有两三个名为“YourProjectName”的东西,当您选择保存新文件的位置时,您必须导航到名为“YourProjectName”的组(文件夹)。不要接受默认的 LO阳离子。
如果操作正确,您将在“YourProjectName”组(文件夹)中看到新文件 User.h/.m 和 Message.h/.m。如果你操作不当,它会是这样的:
看看顶部的文件怎么不在 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
如您所见,您只需要两行代码即可设置 Magical Record,它会依次为您设置 Core Data。
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查询Core Data:
@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
。在 User.m 中,有一行:
#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)可以使用block获取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]);
}