3

OS X (Mac 10.8.2) では、電話番号をオブジェクトABPersonの配列として返すカテゴリを追加しました。オブジェクトには と の 2 つのプロパティがPhoneあります。すべての電話番号を取得し、それらをフィルタリングして、入力に一致するものだけを含めたいと考えています。Phonelabelvaluelabel

valueForKey:s の配列全体、またはキーと ABPersonを持つ個々の人に対してネストされた呼び出しを使用すると、目的の結果が得られます。ただし、s を単一に連結すると、配列全体を検索するときに適切な結果が得られますが、単一の人物オブジェクトを評価するときは得られません@"phones"@"label"valueForKey:valueForKeyPath:

    #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たり、望ましい結果を返す people 配列をフィルタリングするための述語を提供したりできる場合は、非常に感謝しています。

4

1 に答える 1

3

これは のかなり古い問題ですABRecord( 2007 年のこのスレッドを参照してください。おそらく 10.2 の元のコードに戻ると思います。) それらはオーバーライドvalueForKeyPath:され、デフォルトの方法では動作しません。キーは にリストされている必要があり+propertiesます。独自のプロパティを追加できますが、それはあまり役に立ちません。データベースで見つかった結果を返します。あなたのカテゴリメソッドを呼び出すことは決してありません。また、独自のプロパティを追加すると、AB データベース スキーマが変更されるため、とにかく大きな打撃になります。電話番号を別のプロパティにコピーするべきではありません。

私のお勧めは、別のオブジェクトにラップABPersonして、それに対してクエリを実行することです。これにより、レコードをより詳細に制御できます。

ところで、「しかし、それを呼び出すとなぜ機能するのpeopleですか?」という明らかな質問に対して。これは、オーバーライドする別の特別に作成されたバージョンのを使用しているためです。各レコードで呼び出し、 をオーバーライドせず、. 変でしょ?valueForKeyPath:NSArrayvalueForKey:ABPersonvalueForKey:valueForKeyPath:

これにより問題が発生する場合は、レーダーを開く必要があります (bugreport.apple.com)。それがこのようなものを変える唯一の方法です。

これは、同じことを扱った別のブログ投稿ですABPerson彼はそれをラップするのではなく、サブクラス化しています。ABPersonは無料で にブリッジされてABPersonRefおり、サブクラス化できると明示的には言っていないので、おそらく代わりにラッパーを使用します。しかし、ブログの投稿でこの問題についてさらに詳しい洞察が得られるかもしれません。詳細については、この問題に関するこのさらに古いスレッドを参照してください。

于 2013-01-07T21:40:57.670 に答える