我目前正在研究核心数据和新手,从地址簿中获取复合名称和联系号码。我现在可以将数据保存到一个和一个使用KVC和子类化NSManagedObject的多对多关系。但是我仍然不清楚一些概念。
在普通数据库中,我们将数据保存在主数据库中。外键方式。 ContactName表中的ID,compositeName字段以及ContactNumber表中的ID,数字,类型字段。可以在两个表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];
问题:
在KVC方法第3行中,为什么无法创建普通的NSSet?
为什么关系作为NSSet存在,尽管在正常的DB关系中只是一个简单的事情,但在核心数据中似乎很复杂。
我是否正确使用子类生成的方法或者我是否以标准方式使用子类生成的方法? (要求验证我的概念)。
为什么方法#1和方法#2有所不同?两者都在使用子类。
@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;
})
)}
答案 0 :(得分:1)
您将收到NSMutableSet *
,但您可以将其存储在NSSet *
中,因为NSMutableSet *
是NSSet *
的子类。问题是,如果您不打算在集合中添加或删除元素,则应该只执行此操作。
事实上,你可以更容易地做到这一点。你可以这样做:
contactNumberEntity.contactNumberRelation = contactNameEntity;
一般情况下是的,但是你正在编写更多的代码而不是必要的代码,将所有这些中间步骤作为检索集等等......这完全没必要。
我不明白你在这个方向前进的方向。在#2中,您采取中间步骤并将关系存储在NSSet *
类型指针中,然后创建一个新集合NSMutableSet *
,其中第一组中的对象被复制到其中。 #1是从这两种选择中走出来的方式。
我的立场得到了纠正!为了确定这一点,我做了一个实验,发现你不仅要将它们视为两个不同的对象,两个不同方法返回的集合确实有不同的内存地址,因此它们是不同的对象。强>
更新#1
做的原因
NSMutableSet *relationAsSet = [contactNameEntity mutableSetValueForKey:@"contactNameRelation"] ;
是获取已经存在的关系。如果您对现有关系不感兴趣,可以完全跳过此步骤。当然,如果您已经努力将NSManagedObject
子类化,那么您将永远不会这样做。
Xcode将它们生成为NSSet *
,因为Apple更喜欢通过特殊方法更改集合。 核心数据负责维护您的反向关系。为此,他们要求您尊重公共接口并使用这些方法。
采用KVC方法存在细微差别。在那里,他们将返回一个 Key-Value-Observable 的代理对象。这就是为什么你可以改变通过KVC检索的集合的原因,这也是为什么它以NSMutableSet *
的形式返回,让你知道这是可以的。
如果要在该关系中添加或删除单个对象,可以使用:
- (void)addContactNumbersObject:(ContactNumber *)value;
- (void)removeContactNumbersObject:(ContactNumber *)value;
如果您希望替换整套关系,可以使用:
- (void)addContactNumbers:(NSSet *)values;
- (void)removeContactNumbers:(NSSet *)values;
以下是指向Core Data Programming Guide: Using Managed Objects的链接。
@MartinR指出的是,当您想要更改托管对象时,您需要使用Core Data方法提供的直接集或通过KVC。当你这样做时,
NSMutableSet *mSet = [NSMutableSet setWithSet:set];
您实际上正在创建一个全新的集合,该集合将旧集合中的所有对象复制到。我打电话给这个演员是不正确的 - 事实并非如此。
现在是一个非常重要的难题。 我在这里引用Apple文档(也在上面引用):
了解值之间的差异非常重要 由dot访问器和mutableSetValueForKey返回: mutableSetValueForKey:返回一个可变的代理对象。如果你变异 它的内容,它将发出适当的键值观察(KVO) 更改关系的通知。点访问器简单 返回一组。如果您操作此代码中显示的集合 片段:
[aDepartment.employees addObject:newEmployee]; // do not do this!
然后不发出KVO更改通知和反向 关系未正确更新。
报价结束。
永远不要强制将NSManagedObject子类方法中检索到的NSSet *
转换为NSMutableSet *
。
更新#2
扩展了我的解释,说明为什么Xcode会生成返回NSSet *
的访问者,以及为什么不能将其强制转换为NSMutableSet *
来修改关系。
答案 1 :(得分:1)
正如@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)的另一个优点是更好的类型检查。
所以即使我没有直接回答问题,我希望这有助于清理问题!