首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >需要关于核心数据关系概念的帮助,而不是要求任何代码。

需要关于核心数据关系概念的帮助,而不是要求任何代码。
EN

Stack Overflow用户
提问于 2013-04-04 11:45:57
回答 2查看 359关注 0票数 0

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

在普通DB中,我们以主键和外键方式保存数据。ID,compositeName字段在ContactName表中,ID,number,类型字段在ContactNumber表中。可以很容易地在两个表ID字段上创建关系。

在核心数据中,使用KVC实现如下操作:

代码语言:javascript
复制
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:

代码语言:javascript
复制
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的实体子类:

代码语言:javascript
复制
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];

问题:

  1. 在KVC方法第3行中,为什么不能创建正常的NSSet?
  2. 为什么关系以NSSet的形式出现,虽然在正常的DB关系中只是一件简单的事情,但在核心数据中似乎很复杂。
  3. 我是正确地使用子类生成的方法,还是以标准的方式使用子类生成的方法?(要求验证我的概念)。
  4. 为什么方法1和方法2不同?两者都在使用子类。
  5. @property (nonatomic, retain) NSSet *contactNameRelation;NSMutableSet *relationAsSet = [contactNameEntity mutableSetValueForKey:@"contactNameRelation"];是否返回相同的对象/值?

*。请避免任何愚蠢的错误。

*。请告诉我问的问题还不清楚。

编辑1

ContactName实体xcode生成的子类.h文件:

代码语言:javascript
复制
@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文件:

代码语言:javascript
复制
@implementation ContactName

@dynamic compositeName;
@dynamic contactNumbers;

@end

我想知道如何正确地使用这些方法,这就是为什么方法1和方法2是我上面基于这个子类发布的两种不同的方法。什么是对的,什么是不对的,请帮帮我。

编辑2

这就是我记录relationAsSet时得到的

代码语言:javascript
复制
$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 {(
)}

还有这个

代码语言:javascript
复制
$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;
})
)}
EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2013-04-04 21:52:03

正如@svena已经说过的,这些关系的名称有点“非常规”(似乎已经在添加的代码中部分更改了它们)。因此,我将假定以下模型和关系名称:

多到多关系的值是NSSet.例如

代码语言:javascript
复制
ContactName *aContact = ...;
NSSet *relatedNumbers = aContact.contactNumbers;
// or equivalently, using KVC:
relatedNumbers = [aContact valueForKey:@"contactNumbers"];
for (ContactNumber *contactNumber in relatedNumbers) {
    NSLog(@"%@", contactNumber.number);
}

因此,您可以使用关系的值(在本例中为NSSet )遍历对象图并查找相关对象。但是您不能修改这个集(它是不可变的)。

然后你就会有关于

代码语言:javascript
复制
NSMutableSet *mutableRelatedNumbers = [aContact mutableSetValueForKey:@"contactNumbers"];

这是很特别的事情。它是一个“绑定”到aContact对象的可变集。这意味着,如果您修改这个集合(添加或删除某些内容),那么您就有效地修改了contactNumbers对象的aContact关系。

所以

代码语言:javascript
复制
ContactName *aContact = ...;
ContactNumber *aContactNumber1 = ...;
ContactNumber *aContactNumber2 = ...;

// (A)
NSMutableSet *mutableRelatedNumbers = [aContact mutableSetValueForKey:@"contactNumbers"];
[mutableRelatedNumbers addObject:aContactNumber1];
[mutableRelatedNumbers addObject:aContactNumber2];

(这是第一个代码块的更正版本)是一种复杂的方法。

代码语言:javascript
复制
// (B)
[aContact addContactNumbersObject:aContactNumber1];
[aContact addContactNumbersObject:aContactNumber2];

(这是你的方法#1)。另一种等价方法是(由于逆关系)。

代码语言:javascript
复制
// (C)
aContactNumber1.contactName = aContact;
aContactNumber2.contactName = aContact;

代码语言:javascript
复制
// (D)
NSMutableSet *mset = [NSMutableSet set];
[mset addObject:aContactNumber1];
[mset addObject:aContactNumber2];
[aContact addContactNumbers:mset];

它类似于(B),是方法2的修正版本。

这些都是将对象添加到关系中的有效方法,但是(C)是最容易使用的,其次是(B)。(A)和(D)很少使用。(C)的另一个优点是更好的类型检查。

因此,即使我没有直接回答问题,我也希望这有助于澄清问题!

票数 1
EN

Stack Overflow用户

发布于 2013-04-04 12:43:36

  1. 您将收到一个NSMutableSet *,但是您可以将它存储在NSSet *中,因为NSMutableSet *NSSet *的一个子类。问题是,如果您不打算从集合中添加或删除元素,则只应该这样做。
  2. 事实上,你可以更容易地做到这一点。你可以这样做: contactNumberEntity.contactNumberRelation = contactNameEntity;
  3. 一般来说,是的,但是您编写的代码比需要编写的代码多得多,这些中间步骤都是为了检索一个集合,等等。这是完全不必要的。
  4. 我不明白你这次要去哪里。在第二步中,您采取中间步骤,将关系存储在NSSet *类型指针中,然后创建一个新集,即NSMutableSet *,其中包含从第一组复制到其中的对象。第一条是摆脱这两种选择的道路。
  5. 我被纠正了!为了确定这一点,我做了一个实验,发现不仅应该把它们当作两个不同的对象,而且两种不同的方法返回的集合实际上有不同的内存地址,因此它们是不同的对象。

更新#1

做的理由

代码语言:javascript
复制
NSMutableSet *relationAsSet = [contactNameEntity mutableSetValueForKey:@"contactNameRelation"] ;

就是获取已经存在的关系。如果您对现有的关系不感兴趣,可以完全跳过这一步。当然,如果您已经做出了子类NSManagedObject的努力,那么您永远不会想要这样做。

Xcode以NSSet *的形式生成它们,因为苹果更喜欢通过特殊的方法来修改集合。核心数据负责维护您的逆关系。要做到这一点,他们要求您遵守公共接口并使用这些方法。在KVC方法中,有一个细微的差别。在那里,它们将返回一个键值可观测的代理对象。这就是为什么您可以通过KVC对您检索的集合进行变异,这也是为什么它以NSMutableSet *的形式返回,以便让您知道这是可以的。

如果要添加或删除该关系中的单个对象,可以使用:

代码语言:javascript
复制
- (void)addContactNumbersObject:(ContactNumber *)value;
- (void)removeContactNumbersObject:(ContactNumber *)value;

如果您喜欢替换整个关系集,可以使用:

代码语言:javascript
复制
- (void)addContactNumbers:(NSSet *)values;
- (void)removeContactNumbers:(NSSet *)values;

这里有一个指向核心数据编程指南:使用托管对象的链接。

@MartinR所指出的是,当您想要更改您的托管对象时,您需要使用Core数据方法提供的或通过KVC提供的直接集。当你这么做的时候

代码语言:javascript
复制
NSMutableSet *mSet = [NSMutableSet setWithSet:set];

实际上,您正在创建一个全新的集合,它将将旧集中的所有对象复制到。我叫这个演员是不对的-不是的。

现在是拼图中一个非常重要的部分。--我引用苹果的文档(上面也提到了):

理解点访问器和mutableSetValueForKey:返回的值之间的区别是很重要的。mutableSetValueForKey:返回一个可变的代理对象。如果您更改了它的内容,它将为关系发出适当的键值观察(KVO)更改通知。点访问器只返回一个集合。如果如下面的代码片段所示,操作该集:

代码语言:javascript
复制
[aDepartment.employees addObject:newEmployee]; // do not do this!

然后,不会发出KVO更改通知,也不会正确更新逆关系。

of Quote.

永远不要将在NSManagedObject子类方法中检索的NSSet *转换为NSMutableSet *

更新#2

扩展了我关于为什么Xcode生成返回NSSet *的访问器以及为什么不能将它转换到NSMutableSet *来修改关系的解释。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/15810203

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档