3

在 OS X (Mac 10.8.2) 中,我添加了一个类别以ABPerson将电话号码作为对象数组返回Phone。该Phone对象有两个属性,labelvalue。我想获取所有电话号码并过滤它们以仅包含label与输入匹配的电话号码。

valueForKey:如果我在整个ABPersons 数组或带有键的个人上使用嵌套调用@"phones"and @"label",那么我会得到所需的结果。但是,如果我将valueForKey:s 连接到单个valueForKeyPath:,那么在搜索整个数组时我仍然会得到正确的结果,但在评估单个 person 对象时不会

    #import <Cocoa/Cocoa.h>
    #import <AddressBook/AddressBook.h>

    @interface Phone : NSObject
    @property (retain) NSString *label;
    @property (retain) NSString *value;
    @end

    @implementation Phone
    - (void)dealloc {
        self.label = nil;
        self.value = nil;
        [super dealloc];
    }
    @end

    @interface ABPerson (phones)
    - (NSArray *)phones;
    @end

    @implementation ABPerson (phones)
    - (NSArray *)phones {
        NSMutableArray *phones = [NSMutableArray array];
        ABMultiValue *values = [self valueForProperty:kABPhoneProperty];
        for (NSInteger i = 0; i < [values count]; i++) {
            Phone *phone = [[Phone alloc] init];
            phone.label = ABLocalizedPropertyOrLabel([values labelAtIndex:i]);
            phone.value = [values valueAtIndex:i];
            [phones addObject:phone];
            [phone release];
        }
        return [NSArray arrayWithArray:phones];
    }
    @end

    int main(int argc, char *argv[]) {
        NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];

        NSArray *people = [[ABAddressBook sharedAddressBook] people];
        ABPerson *person = [people objectAtIndex:0];

        NSArray *a0 = [[people valueForKey:@"phones"] valueForKey:@"label"];
        NSArray *a1 = [people valueForKeyPath:@"phones.label"];
        NSLog(@"a0 == a1: %d", [a0 isEqualTo:a1]);

        NSArray *a2 = [[person valueForKey:@"phones"] valueForKey:@"label"];
        NSArray *a3 = [person valueForKeyPath:@"phones.label"];
        NSLog(@"a2 == a3: %d", [a2 isEqualTo:a3]);

        NSLog(@"people count                                         : %3ld", [people count]);
        NSLog(@"people phones label count   (valueForKey.valueForKey): %3ld", [a0 count]);
        NSLog(@"people phones label count           (valueForKeyPath): %3ld", [a1 count]);
        NSLog(@"person phones label count   (valueForKey.valueForKey): %3ld", [a2 count]);
        NSLog(@"person phones label count           (valueForKeyPath): %3ld", [a3 count]);
        NSLog(@"----------------------------------------------------------");
        NSLog(@"people phones label objectAtIndex:0 (valueForKeyPath): %@", [a1 objectAtIndex:0]);
        NSLog(@"person phones label         (valueForKey.valueForKey): %@", a2);
        NSLog(@"person phones label                 (valueForKeyPath): %@", a3);

        [pool release];
        return 0;
    }

这输出:

2013-01-07 15:03:42.537 test[51919:303] a0 == a1: 1
2013-01-07 15:03:42.540 test[51919:303] a2 == a3: 0
2013-01-07 15:03:42.541 test[51919:303] people count                                         : 200
2013-01-07 15:03:42.543 test[51919:303] people phones label count   (valueForKey.valueForKey): 200
2013-01-07 15:03:42.544 test[51919:303] people phones label count           (valueForKeyPath): 200
2013-01-07 15:03:42.545 test[51919:303] person phones label count   (valueForKey.valueForKey):   2
2013-01-07 15:03:42.546 test[51919:303] person phones label count           (valueForKeyPath):   0
2013-01-07 15:03:42.547 test[51919:303] ----------------------------------------------------------
2013-01-07 15:03:42.548 test[51919:303] people phones label objectAtIndex:0 (valueForKeyPath): (
    mobile,
    home
)
2013-01-07 15:03:42.549 test[51919:303] person phones label         (valueForKey.valueForKey): (
    mobile,
    home
)
2013-01-07 15:03:42.551 test[51919:303] person phones label                 (valueForKeyPath): (null)

为什么不[[person valueForKey:@"phones"] valueForKey:@"label"]等于[person valueForKeyPath:@"phones.label"]?这很重要,因为我想使用完整的@"phone.label"密钥路径对所有联系人使用谓词过滤器,如下所示:

    NSString *value = @"home";
    NSPredicate *predicate = [NSPredicate predicateWithFormat:@"ANY phones.label == %@", value];
    NSArray *a4 = [people filteredArrayUsingPredicate:predicate];

在此示例中,a4始终为空(使用不带“ANY”标志的谓词仍会导致过滤后的数组为空)。我相信这是因为filteredArrayUsingPredicate:本质上是在评估每个对象(人),valueForKeyPath:并且如上所述,这不适用于单个人对象。它是否正确?

如果有人可以阐明对象失败valueForKeyPath:的原因ABPerson,或者提供一个谓词来过滤返回所需结果的人员数组,我将不胜感激。

4

1 回答 1

3

这是一个相当古老的问题ABRecord(从 2007 年开始查看这个线程;我相信可能会回到 10.2 的原始代码。)它们会覆盖valueForKeyPath:并且它不会以默认方式运行。密钥必须列在+properties. 虽然您可以添加自己的属性,但这并没有真正的帮助。它将返回在数据库中找到的结果。它永远不会调用您的类别方法。添加您自己的属性会修改 AB 数据库模式,因此无论如何它都是一个非常大的锤子。您绝对不应该将电话号码复制到单独的属性中。

我的建议是包装ABPerson到另一个对象中并对其执行查询。这将使您对记录有更多的控制权。

顺便说一句,对于一个明显的问题“但是为什么当你调用它时它会起作用people?” 那是因为您正在使用另一个专门编写的版本valueForKeyPath:,即NSArray覆盖的版本。它调用valueForKey:每条记录,并且ABPerson不会覆盖valueForKey:,只是valueForKeyPath:. 很奇怪吧?

如果这给您带来麻烦,您应该打开一个雷达 (bugreport.apple.com)。只有这样才能改变这种情况。

这是另一篇针对同一问题的博客文章。他是子类化ABPerson而不是包装它。由于ABPersontoll-free 桥接到ABPersonRef,并且没有明确说您可以对其进行子类化,因此我可能会改用包装器。但是博客文章可能会让您对这个问题有更多的了解,并且子类化可能很好。有关更多信息,请参阅有关此问题的更古老的线程

于 2013-01-07T21:40:57.670 回答