0

我目前正在研究核心数据和新手,从通讯录中获取复合名称和联系电话。我现在可以使用 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];

问题:

  1. KVC方法第3行,为什么不能创建正常的NSSet?

  2. 为什么关系以 NSSet 的形式存在,虽然在正常的数据库关系中只是一件简单的事情,但在核心数据中似乎很复杂。

  3. 我是正确使用子类生成的方法还是以标准方式使用子类生成的方法?(要求验证我的概念)。

  4. 为什么方法#1 和方法#2 不同?两者都在使用子类。

  5. @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;
})
)}
4

2 回答 2

1
  1. 您将收到一个NSMutableSet *,但您可以将其存储在其中,NSSet *因为它NSMutableSet *是 的子类NSSet *。问题是,如果您不打算从集合中添加或删除元素,您应该只这样做。

  2. 事实上,你可以更容易地做到这一点。你可以这样做:

    contactNumberEntity.contactNumberRelation = contactNameEntity;

  3. 一般来说是的,但是您编写的代码比将所有这些中间步骤作为检索集合等所需的代码多得多……这完全没有必要。

  4. 我不明白你要带着这个去哪里。在#2 中,您采取中间步骤并将关系存储在NSSet *类型指针中,然后创建一个新集合,NSMutableSet *将第一个集合中的对象复制到其中。#1是从这两种选择中走出来的路。

  5. 我站纠正!为了确定这一点,我做了一个实验,发现不仅应该将它们视为两个不同的对象,两种不同方法返回的集合实际上具有不同的内存地址,因此它们是不同的对象。

更新#1

做的理由

NSMutableSet *relationAsSet = [contactNameEntity mutableSetValueForKey:@"contactNameRelation"] ;

是获取已经存在的关系。如果您对现有关系不感兴趣,则可以完全跳过此步骤。当然,如果您已经努力将您的 子类化NSManagedObject,您将永远不想这样做。

Xcode 将它们生成为NSSet *,因为 Apple 更喜欢您通过特殊方法更改集合。Core Data负责维护您的反向关系。为此,他们要求您尊重公共接口并使用这些方法。与 KVC 方法有细微的差别。在那里,他们将返回一个Key-Value-Observable代理对象。这就是为什么你可以改变你通过 KVC 检索到的集合,这也是它返回为 的原因NSMutableSet *,让你知道这没关系。

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

- (void)addContactNumbersObject:(ContactNumber *)value;
- (void)removeContactNumbersObject:(ContactNumber *)value;

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

- (void)addContactNumbers:(NSSet *)values;
- (void)removeContactNumbers:(NSSet *)values;

这是核心数据编程指南的链接:使用托管对象

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

NSMutableSet *mSet = [NSMutableSet setWithSet:set];

您实际上是在创建一个全新的集合,它将旧集合中的所有对象复制到其中。而且我称这个铸造是不正确的 - 它不是。

现在是一个非常重要的难题。 我在这里引用 Apple 文档(上面也引用过):

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

[aDepartment.employees addObject:newEmployee]; // do not do this!

然后不会发出 KVO 更改通知,并且不会正确更新反向关系。

报价结束。

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

更新#2

扩展了我对为什么 Xcode 生成返回的访问器NSSet *以及为什么你不能将其强制转换NSMutableSet *为修改关系的解释。

于 2013-04-04T12:43:36.487 回答
1

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

核心数据模型

一对多关系的值是 a NSSet。例如

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

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

然后你有问题

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-04T21:52:03.200 回答