Perbedaan antara objectForKey dan valueForKey?

345

Apa perbedaan antara objectForKeydan valueForKey? Saya melihat keduanya dalam dokumentasi dan mereka tampak sama bagi saya.

Berbakti
sumber

Jawaban:

403

objectForKey:adalah sebuah NSDictionarymetode. An NSDictionaryadalah kelas koleksi yang mirip dengan NSArray, kecuali alih-alih menggunakan indeks, itu menggunakan kunci untuk membedakan antara item. Kunci adalah string acak yang Anda berikan. Tidak ada dua objek yang dapat memiliki kunci yang sama (sama seperti tidak ada dua objek dalam suatu NSArraydapat memiliki indeks yang sama).

valueForKey:adalah metode KVC. Ini bekerja dengan kelas APA SAJA. valueForKey:memungkinkan Anda mengakses properti menggunakan string untuk namanya. Jadi misalnya, jika saya memiliki Accountkelas dengan properti accountNumber, saya dapat melakukan hal berikut:

NSNumber *anAccountNumber = [NSNumber numberWithInt:12345];
Account *newAccount = [[Account alloc] init];

[newAccount setAccountNumber:anAccountNUmber];

NSNumber *anotherAccountNumber = [newAccount accountNumber];

Dengan menggunakan KVC, saya dapat mengakses properti secara dinamis:

NSNumber *anAccountNumber = [NSNumber numberWithInt:12345];
Account *newAccount = [[Account alloc] init];

[newAccount setValue:anAccountNumber forKey:@"accountNumber"];

NSNumber *anotherAccountNumber = [newAccount valueForKey:@"accountNumber"];

Itu adalah set pernyataan yang setara.

Saya tahu Anda berpikir: wow, tetapi dengan sarkastis. KVC tidak terlalu berguna. Bahkan, itu terlihat "bertele-tele". Tetapi ketika Anda ingin mengubah hal-hal saat runtime, Anda dapat melakukan banyak hal keren yang jauh lebih sulit dalam bahasa lain (tapi ini di luar ruang lingkup pertanyaan Anda).

Jika Anda ingin mempelajari lebih lanjut tentang KVC, ada banyak tutorial jika Anda Google terutama di blog Scott Stevenson . Anda juga dapat melihat Referensi Protokol NSKeyValueCoding .

Semoga itu bisa membantu.

Corey Floyd
sumber
12
valueForKey berperilaku berbeda untuk objek NSDictionary tergantung pada apakah kunci dimulai dengan simbol @.
dreamlax
61
objectForKey: menerima objek apa pun sebagai kunci, bukan hanya string. Satu-satunya persyaratan adalah bahwa kunci mendukung protokol NSCopying.
Ashley Clark
5
Saya terkejut tidak ada yang mengoreksi jawaban ini dengan menunjukkan valueForKey: secara teknis tidak memberi Anda akses ke variabel instance terkait, melainkan metode accessor yang (dapat) mengelola variabel instance.
Dany Joumaa
7
Peringatan: valueForKey bisa sangat lambat - saat ini merupakan hambatan utama dalam aplikasi iPad saya, sangat lambat sehingga menggantinya dengan kamus "standar" membuat aplikasi terasa lebih cepat. Sesuatu yang sangat salah dengan KVC di iOS, dan saya tidak akan pernah menggunakannya lagi - tidak sepadan dengan penurunan kinerja, dan harus menulis ulangnya jauh-jauh. Ini menggunakan kunci NSString dengan nilai NSString pada CALayers. Instrumen menunjukkan bahwa "CAObject_valueForKey" adalah 25% dari total runtime (!)
Adam
2
@Adam Itu terdengar menakutkan. Sudahkah Anda mencobanya lagi sejak iOS7? Jika demikian, apakah sudah berubah sejak saat itu?
Unheilig
64

Ketika Anda melakukannya, valueForKey:Anda perlu memberinya NSString, sedangkan objectForKey:dapat mengambil subkelas NSObject sebagai kunci. Ini karena untuk Pengodean Nilai-Kunci, kunci selalu berupa string.

Bahkan, dokumentasi menyatakan bahwa bahkan ketika Anda memberikan valueForKey:NSString, ia objectForKey:tetap akan memohon kecuali jika string dimulai dengan @, dalam hal ini ia memanggil [super valueForKey:], yang dapat memanggil valueForUndefinedKey:yang dapat menimbulkan pengecualian.

dreamlax
sumber
tolong beri saya tautan dokumentasi yang Anda maksud. Terima kasih.
Ali Amin
5
@ عليامين: Ada di sini
dreamlax
20

Berikut adalah alasan yang bagus untuk digunakan objectForKey:sedapat mungkin alih-alih valueForKey:- valueForKey:dengan kunci yang tidak dikenal akan membuang kata NSUnknownKeyException"kelas ini tidak sesuai dengan kode kunci untuk kunci".

Nick Locking
sumber
5
baik untuk mengetahui bahwa "valueForKey: dengan kunci yang tidak dikenal akan melempar NSUnknownKeyException yang mengatakan" kelas ini bukan nilai kunci yang sesuai dengan kode kunci "
onmyway133
Ini tidak benar dengan NSDictionary, dan Anda dapat mencoba ini: NSLog (@ "Z:% @", [@ {@ "X": @ (10), @ "Y": @ (20)} valueForKey: @ "Z"]); valueForKey akan mengeluarkan pengecualian seperti itu pada kelas lain yang tidak mendukung kunci yang ditentukan - tetapi untuk subkelas NSDictionary - Anda hanya akan menerima nil yang tenang. coba ini:
Motti Shneor
13

Seperti yang dikatakan, objectForKey:tipe data adalah :(id)aKeysedangkan valueForKey:tipe data :(NSString *)key.

Sebagai contoh:

 NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys:[NSArray arrayWithObject:@"123"],[NSNumber numberWithInteger:5], nil];

 NSLog(@"objectForKey : --- %@",[dict objectForKey:[NSNumber numberWithInteger:5]]);  
    //This will work fine and prints (    123    )  

 NSLog(@"valueForKey  : --- %@",[dict valueForKey:[NSNumber numberWithInteger:5]]); 
    //it gives warning "Incompatible pointer types sending 'NSNumber *' to parameter of type 'NSString *'"   ---- This will crash on runtime. 

Jadi, valueForKey:hanya akan mengambil nilai string dan merupakan metode KVC, sedangkan objectForKey:akan mengambil semua jenis objek.

Nilai dalam objectForKeyakan diakses oleh jenis objek yang sama.

Harjot Singh
sumber
0

Saya akan mencoba memberikan jawaban yang komprehensif di sini. Sebagian besar poin muncul dalam jawaban lain, tetapi saya menemukan setiap jawaban tidak lengkap, dan beberapa salah.

Pertama dan terpenting, objectForKey:adalah NSDictionarymetode, sedangkan valueForKey:metode protokol KVC diperlukan dari setiap kelas keluhan KVC - termasuk NSDictionary.

Selanjutnya, seperti @dreamlax menulis, petunjuk dokumentasi yang NSDictionarymengimplementasikan nya valueForKey:metode MENGGUNAKAN nya objectForKey:implementasi. Dengan kata lain - [NSDictionary valueForKey:]panggilan [NSDictionary objectForKey:].

Ini menyiratkan, yang valueForKey:tidak pernah bisa lebih cepat daripada objectForKey:(pada tombol input yang sama) meskipun pengujian menyeluruh yang telah saya lakukan menyiratkan sekitar 5% hingga 15% perbedaan, lebih dari miliaran akses acak ke NSDictionary yang sangat besar. Dalam situasi normal - perbedaannya dapat diabaikan.

Berikutnya: Protokol KVC hanya berfungsi dengan NSString *kunci, karenanya valueForKey:hanya akan menerima kunci NSString *(atau subkelas) sebagai kunci, sementara NSDictionarydapat bekerja dengan jenis objek lain sebagai kunci - sehingga "level bawah" objectForKey:menerima objek apa pun yang dapat disalin (sesuai protokol NSCopying) sebagai kunci.

Terakhir, NSDictionary'simplementasi penyimpangan dari valueForKey:perilaku standar yang didefinisikan dalam dokumentasi KVC, dan TIDAK akan memancarkan NSUnknownKeyExceptionuntuk kunci yang tidak dapat ditemukan - kecuali ini adalah kunci "khusus" - yang dimulai dengan '@' - yang biasanya berarti " kunci fungsi agregasi (misalnya @"@sum, @"@avg"). Sebagai gantinya, itu hanya akan mengembalikan nol ketika kunci tidak ditemukan di NSDictionary - berperilaku sama denganobjectForKey:

Berikut ini adalah beberapa kode uji untuk menunjukkan dan membuktikan catatan saya.

- (void) dictionaryAccess {
    NSLog(@"Value for Z:%@", [@{@"X":@(10), @"Y":@(20)} valueForKey:@"Z"]); // prints "Value for Z:(null)"

    uint32_t testItemsCount = 1000000;
    // create huge dictionary of numbers
    NSMutableDictionary *d = [NSMutableDictionary dictionaryWithCapacity:testItemsCount];
    for (long i=0; i<testItemsCount; ++i) {
        // make new random key value pair:
        NSString *key = [NSString stringWithFormat:@"K_%u",arc4random_uniform(testItemsCount)];
        NSNumber *value = @(arc4random_uniform(testItemsCount));
        [d setObject:value forKey:key];
    }
    // create huge set of random keys for testing.
    NSMutableArray *keys = [NSMutableArray arrayWithCapacity:testItemsCount];
    for (long i=0; i<testItemsCount; ++i) {
        NSString *key = [NSString stringWithFormat:@"K_%u",arc4random_uniform(testItemsCount)];
        [keys addObject:key];
    }

    NSDictionary *dict = [d copy];
    NSTimeInterval vtotal = 0.0, ototal = 0.0;

    NSDate *start;
    NSTimeInterval elapsed;

    for (int i = 0; i<10; i++) {

        start = [NSDate date];
        for (NSString *key in keys) {
            id value = [dict valueForKey:key];
        }
        elapsed = [[NSDate date] timeIntervalSinceDate:start];
        vtotal+=elapsed;
        NSLog (@"reading %lu values off dictionary via valueForKey took: %10.4f seconds", keys.count, elapsed);

        start = [NSDate date];
        for (NSString *key in keys) {
            id obj = [dict objectForKey:key];
        }
        elapsed = [[NSDate date] timeIntervalSinceDate:start];
        ototal+=elapsed;
        NSLog (@"reading %lu objects off dictionary via objectForKey took: %10.4f seconds", keys.count, elapsed);
    }

    NSString *slower = (vtotal > ototal) ? @"valueForKey" : @"objectForKey";
    NSString *faster = (vtotal > ototal) ? @"objectForKey" : @"valueForKey";
    NSLog (@"%@ takes %3.1f percent longer then %@", slower, 100.0 * ABS(vtotal-ototal) / MAX(ototal,vtotal), faster);
}
Motti Shneor
sumber