我目前正在研究核心数据和新手,从地址簿中获取复合名称和联系人号码。现在,我可以使用KVC和子类NSManagedObject将数据保存到一对多的关系中。但有些概念对我来说还不清楚。

在普通DB中,我们以主键和外键方式保存数据。ID,compositeName字段在ContactName表中,ID,number,类型字段在ContactNumber表中。可以很容易地在两个表ID字段上创建关系。
在核心数据中,使用KVC实现如下操作:
1. NSManagedObject *contactNameEntity = [NSEntityDescription insertNewObjectForEntityForName:@"ContactName" inManagedObjectContext:self.managedObjectContext];
2. [contactNameEntity setValue:@"TheName" forKey:@"compositeName"]
3. NSMutableSet *relationAsSet = [contactNameEntity mutableSetValueForKey:@"contactNameRelation"];
//fetching multi values like contact mobile, iPhone, home, work, other numbers and iterating them
4. ABMutableMultiValueRef multiValues = ABRecordCopyValue(ref, kABPersonPhoneProperty);
5. CFArrayRef mVArray = ABMultiValueCopyArrayOfAllValues(multiValues);
6. for(int i=0; i<CFArrayGetCount(mVArray); i++) {
7. NSManagedObject *contactNumberEntity = [NSEntityDescription insertNewObjectForEntityForName:@"ContactNumber" inManagedObjectContext:self.managedObjectContext];
8. [contactNumberEntity setValue:(__bridge NSString*)CFArrayGetValueAtIndex(mVArray, i) forKey:@"number"];
9. [contactNumberEntity setValue:(__bridge id)(ABMultiValueCopyLabelAtIndex(multiValues, i)) forKey:@"type"];
10. [relationAsSet addObject:contactNumberEntity];
}
11. [contactNameEntity setValue:relationAsSet forKey:@"contactNameRelation"];现在使用实体子类方法#1:
1. ContactName *contactNameEntity = [NSEntityDescription insertNewObjectForEntityForName:@"ContactName" inManagedObjectContext:self.managedObjectContext];
2. [contactNameEntity setCompositeName:(__bridge id)(compositeName)];
3. for(int i=0; i<CFArrayGetCount(mVArray); i++) {
4. ContactNumber *contactNumberEntity = [NSEntityDescription
5. insertNewObjectForEntityForName:@"ContactNumber" inManagedObjectContext:self.managedObjectContext];
6. [contactNumberEntity setNumber:(__bridge NSString*)CFArrayGetValueAtIndex(mVArray, i)];
7. [contactNumberEntity setType:(__bridge id)(ABMultiValueCopyLabelAtIndex(multiValues, i))];
8. [contactNameEntity addContactNameRelationObject:contactNumberEntity];
}现在使用类似于KVC方法#2的实体子类:
1. ContactName *contactNameEntity = [NSEntityDescription insertNewObjectForEntityForName:@"ContactName" inManagedObjectContext:self.managedObjectContext];
2. [contactNameEntity setCompositeName:(__bridge id)(compositeName)];
3. NSSet *set = [contactNameEntity contactNameRelation];
4. NSMutableSet *mSet = [NSMutableSet setWithSet:set];
5. set = nil;
6. for(int i=0; i<CFArrayGetCount(mVArray); i++) {
7. ContactNumber *contactNumberEntity = [NSEntityDescription
8. insertNewObjectForEntityForName:@"ContactNumber" inManagedObjectContext:self.managedObjectContext];
9. [contactNumberEntity setNumber:(__bridge NSString*)CFArrayGetValueAtIndex(mVArray, i)];
10. [contactNumberEntity setType:(__bridge id)(ABMultiValueCopyLabelAtIndex(multiValues, i))];
11. [mSet addObject:contactNumberEntity];
}
12. [contactNameEntity addContactNameRelation:mSet];问题:
@property (nonatomic, retain) NSSet *contactNameRelation;和NSMutableSet *relationAsSet = [contactNameEntity mutableSetValueForKey:@"contactNameRelation"];是否返回相同的对象/值?*。请避免任何愚蠢的错误。
*。请告诉我问的问题还不清楚。
编辑1
ContactName实体xcode生成的子类.h文件:
@property (nonatomic, retain) NSString * number;
@property (nonatomic, retain) NSSet *contactNumbers;
@end
@interface ContactName (CoreDataGeneratedAccessors)
- (void)addContactNumbersObject:(ContactNumber *)value;
- (void)removeContactNumbersObject:(ContactNumber *)value;
- (void)addContactNumbers:(NSSet *)values;
- (void)removeContactNumbers:(NSSet *)values;这是.m文件:
@implementation ContactName
@dynamic compositeName;
@dynamic contactNumbers;
@end我想知道如何正确地使用这些方法,这就是为什么方法1和方法2是我上面基于这个子类发布的两种不同的方法。什么是对的,什么是不对的,请帮帮我。
编辑2
这就是我记录relationAsSet时得到的
$0 = 0x0854c1c0 Relationship 'contactNameRelation' on managed object (0x835bae0) <ContactName: 0x835bae0> (entity: ContactName; id: 0x835ba80 <x-coredata:///ContactName/t66752D00-F08B-40DF-AEED-ABCACEA254652> ; data: {
compositeName = myname;
contactNameRelation = (
);
}) with objects {(
)}还有这个
$0 = 0x0821b460 Relationship 'contactNameRelation' on managed object (0x823fe50) <ContactName: 0x823fe50> (entity: ContactName; id: 0x823fea0 <x-coredata:///ContactName/t3CED08C3-94E0-4B43-93F0-96DE2D2A24922> ; data: {
compositeName = myname;
contactNameRelation = (
"0x821be40 <x-coredata:///ContactNumber/t3CED08C3-94E0-4B43-93F0-96DE2D2A24923>"
);
}) with objects {(
<ContactNumber: 0x8241ab0> (entity: ContactNumber; id: 0x821be40 <x-coredata:///ContactNumber/t3CED08C3-94E0-4B43-93F0-96DE2D2A24923> ; data: {
contactNumberRelation = "0x823fea0 <x-coredata:///ContactName/t3CED08C3-94E0-4B43-93F0-96DE2D2A24922>";
number = 123;
type = iphone;
})
)}发布于 2013-04-04 21:52:03
正如@svena已经说过的,这些关系的名称有点“非常规”(似乎已经在添加的代码中部分更改了它们)。因此,我将假定以下模型和关系名称:

多到多关系的值是NSSet.例如
ContactName *aContact = ...;
NSSet *relatedNumbers = aContact.contactNumbers;
// or equivalently, using KVC:
relatedNumbers = [aContact valueForKey:@"contactNumbers"];
for (ContactNumber *contactNumber in relatedNumbers) {
NSLog(@"%@", contactNumber.number);
}因此,您可以使用关系的值(在本例中为NSSet )遍历对象图并查找相关对象。但是您不能修改这个集(它是不可变的)。
然后你就会有关于
NSMutableSet *mutableRelatedNumbers = [aContact mutableSetValueForKey:@"contactNumbers"];这是很特别的事情。它是一个“绑定”到aContact对象的可变集。这意味着,如果您修改这个集合(添加或删除某些内容),那么您就有效地修改了contactNumbers对象的aContact关系。
所以
ContactName *aContact = ...;
ContactNumber *aContactNumber1 = ...;
ContactNumber *aContactNumber2 = ...;
// (A)
NSMutableSet *mutableRelatedNumbers = [aContact mutableSetValueForKey:@"contactNumbers"];
[mutableRelatedNumbers addObject:aContactNumber1];
[mutableRelatedNumbers addObject:aContactNumber2];(这是第一个代码块的更正版本)是一种复杂的方法。
// (B)
[aContact addContactNumbersObject:aContactNumber1];
[aContact addContactNumbersObject:aContactNumber2];(这是你的方法#1)。另一种等价方法是(由于逆关系)。
// (C)
aContactNumber1.contactName = aContact;
aContactNumber2.contactName = aContact;或
// (D)
NSMutableSet *mset = [NSMutableSet set];
[mset addObject:aContactNumber1];
[mset addObject:aContactNumber2];
[aContact addContactNumbers:mset];它类似于(B),是方法2的修正版本。
这些都是将对象添加到关系中的有效方法,但是(C)是最容易使用的,其次是(B)。(A)和(D)很少使用。(C)的另一个优点是更好的类型检查。
因此,即使我没有直接回答问题,我也希望这有助于澄清问题!
发布于 2013-04-04 12:43:36
NSMutableSet *,但是您可以将它存储在NSSet *中,因为NSMutableSet *是NSSet *的一个子类。问题是,如果您不打算从集合中添加或删除元素,则只应该这样做。NSSet *类型指针中,然后创建一个新集,即NSMutableSet *,其中包含从第一组复制到其中的对象。第一条是摆脱这两种选择的道路。更新#1
做的理由
NSMutableSet *relationAsSet = [contactNameEntity mutableSetValueForKey:@"contactNameRelation"] ;就是获取已经存在的关系。如果您对现有的关系不感兴趣,可以完全跳过这一步。当然,如果您已经做出了子类NSManagedObject的努力,那么您永远不会想要这样做。
Xcode以NSSet *的形式生成它们,因为苹果更喜欢通过特殊的方法来修改集合。核心数据负责维护您的逆关系。要做到这一点,他们要求您遵守公共接口并使用这些方法。在KVC方法中,有一个细微的差别。在那里,它们将返回一个键值可观测的代理对象。这就是为什么您可以通过KVC对您检索的集合进行变异,这也是为什么它以NSMutableSet *的形式返回,以便让您知道这是可以的。
如果要添加或删除该关系中的单个对象,可以使用:
- (void)addContactNumbersObject:(ContactNumber *)value;
- (void)removeContactNumbersObject:(ContactNumber *)value;如果您喜欢替换整个关系集,可以使用:
- (void)addContactNumbers:(NSSet *)values;
- (void)removeContactNumbers:(NSSet *)values;这里有一个指向核心数据编程指南:使用托管对象的链接。
@MartinR所指出的是,当您想要更改您的托管对象时,您需要使用Core数据方法提供的或通过KVC提供的直接集。当你这么做的时候
NSMutableSet *mSet = [NSMutableSet setWithSet:set];实际上,您正在创建一个全新的集合,它将将旧集中的所有对象复制到。我叫这个演员是不对的-不是的。
现在是拼图中一个非常重要的部分。--我引用苹果的文档(上面也提到了):
理解点访问器和mutableSetValueForKey:返回的值之间的区别是很重要的。mutableSetValueForKey:返回一个可变的代理对象。如果您更改了它的内容,它将为关系发出适当的键值观察(KVO)更改通知。点访问器只返回一个集合。如果如下面的代码片段所示,操作该集:
[aDepartment.employees addObject:newEmployee]; // do not do this!然后,不会发出KVO更改通知,也不会正确更新逆关系。
of Quote.
永远不要将在NSManagedObject子类方法中检索的NSSet *转换为NSMutableSet *。
更新#2
扩展了我关于为什么Xcode生成返回NSSet *的访问器以及为什么不能将它转换到NSMutableSet *来修改关系的解释。
https://stackoverflow.com/questions/15810203
复制相似问题