ความแตกต่างระหว่าง objectForKey และ valueForKey?


คำตอบ:


403

objectForKey:เป็นNSDictionaryวิธีการ An NSDictionaryเป็นคลาสคอลเลกชันที่คล้ายกับNSArrayยกเว้นแทนที่จะใช้ดัชนีจะใช้คีย์เพื่อแยกความแตกต่างระหว่างรายการ คีย์คือสตริงที่คุณกำหนดเอง ไม่มีวัตถุใดที่สามารถมีคีย์เดียวกันได้ (เช่นเดียวกับที่วัตถุสองอันในNSArrayไม่สามารถมีดัชนีเดียวกันได้)

valueForKey:เป็นวิธี KVC มันทำงานร่วมกับชั้นเรียนใด ๆ valueForKey:อนุญาตให้คุณเข้าถึงคุณสมบัติโดยใช้สตริงสำหรับชื่อ ตัวอย่างเช่นถ้าฉันมีAccountคลาสที่มีคุณสมบัติaccountNumberฉันสามารถทำสิ่งต่อไปนี้:

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

[newAccount setAccountNumber:anAccountNUmber];

NSNumber *anotherAccountNumber = [newAccount accountNumber];

ใช้ KVC ฉันสามารถเข้าถึงคุณสมบัติแบบไดนามิก:

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

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

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

สิ่งเหล่านี้คือชุดข้อความที่เทียบเท่ากัน

ฉันรู้ว่าคุณกำลังคิด: ว้าว แต่ประชดประชัน KVC ไม่ได้มีประโยชน์อะไรเลย ที่จริงแล้วมันดูเหมือน "คำพูด" แต่เมื่อคุณต้องการเปลี่ยนสิ่งต่าง ๆ ที่รันไทม์คุณสามารถทำสิ่งดีๆมากมายที่ยากขึ้นในภาษาอื่น ๆ (แต่นี่ไม่ใช่ขอบเขตของคำถามของคุณ)

หากคุณต้องการที่จะเรียนรู้เพิ่มเติมเกี่ยวกับ KVC มีบทเรียนหลายถ้าคุณ Google โดยเฉพาะอย่างยิ่งในบล็อกของสกอตต์ของสตีเวนสัน นอกจากนี้คุณยังสามารถตรวจสอบการอ้างอิงโปรโตคอล NSKeyValueCoding

หวังว่าจะช่วย


12
valueForKey ทำงานต่างกันสำหรับวัตถุ NSDictionary ขึ้นอยู่กับว่าคีย์เริ่มต้นด้วยสัญลักษณ์ @
dreamlax

61
objectForKey: ยอมรับวัตถุใด ๆ เป็นคีย์ไม่ใช่แค่สตริง ข้อกำหนดเพียงอย่างเดียวคือกุญแจสนับสนุนโปรโตคอล NSCopying
แอชลีย์คลาร์ก

5
ฉันประหลาดใจที่ไม่มีใครแก้ไขคำตอบนี้ได้โดยชี้ให้เห็นว่า value forKey: ในทางเทคนิคแล้วไม่ให้คุณเข้าถึงตัวแปรอินสแตนซ์ที่เกี่ยวข้อง แต่เป็นวิธีการเข้าถึงที่จัดการได้
Dany Joumaa

7
คำเตือน: valueForKey อาจช้ามากปัจจุบันเป็นคอขวดที่สำคัญในแอพ iPad ของฉันดังนั้นช้าที่แทนที่ด้วยพจนานุกรม "มาตรฐาน" ทำให้แอปเร็วขึ้นอย่างเห็นได้ชัด มีบางอย่างผิดปกติกับ KVC บน iOS และฉันจะไม่ใช้อีกเลย - ไม่คุ้มกับประสิทธิภาพที่ลดลงและต้องเขียนใหม่อีกครั้ง นี่คือการใช้คีย์ NSString กับค่า NSString บน CALayers เครื่องมือแสดงให้เห็นว่า "CAObject_valueForKey" คือ 25% ของรันไทม์ทั้งหมด (!)
Adam

2
@ อดัมฟังดูน่ากลัว คุณลองใหม่อีกครั้งตั้งแต่ iOS7? ถ้าเป็นเช่นนั้นสิ่งต่าง ๆ มีการเปลี่ยนแปลงตั้งแต่?
Unheilig

64

เมื่อคุณvalueForKey:ต้องให้ NSString ในขณะที่objectForKey:สามารถใช้คลาสย่อย NSObject เป็นคีย์ได้ นี่เป็นเพราะสำหรับการเข้ารหัสคีย์ - ค่าคีย์จะเป็นสตริงเสมอ

ในความเป็นจริงเอกสารระบุว่าแม้ว่าคุณจะให้valueForKey:NSString มันก็จะเรียกใช้objectForKey:ต่อไปเว้นแต่ว่าสตริงเริ่มต้นด้วย@ในกรณีที่มันเรียก[super valueForKey:]ซึ่งอาจเรียกvalueForUndefinedKey:ซึ่งอาจยกข้อยกเว้น


คุณกรุณาให้ลิงค์ของเอกสารที่คุณหมายถึง ขอบคุณ.
Ali Amin


20

นี่เป็นเหตุผลที่ดีที่จะใช้objectForKey:เป็นไปได้แทนvalueForKey:- valueForKey:มีคีย์ที่ไม่รู้จักจะโยนNSUnknownKeyExceptionว่า "ระดับนี้ไม่ได้เป็นค่าคีย์การเข้ารหัสที่สอดคล้องสำหรับคีย์"


5
ดีใจที่ได้ทราบว่า "valueForKey: ด้วยคีย์ที่ไม่รู้จักจะโยน NSUnknownKeyException โดยพูดว่า" คลาสนี้ไม่ใช่ค่าคีย์ที่เข้ารหัสตามคีย์ "
onmyway133

นี่ไม่ใช่เรื่องจริงสำหรับ NSDictionary และคุณสามารถลองได้ที่: NSLog (@ "Z:% @", [@ {@ "X": @ (10), @ "Y": @ (20)} valueForKey: @ "Z"]); valueForKey จะปล่อยข้อยกเว้นดังกล่าวในคลาสอื่น ๆ ที่ไม่สนับสนุนคีย์ที่ระบุ - แต่สำหรับคลาสย่อย NSDictionary - คุณจะได้รับศูนย์ที่เงียบ ลองนี้:
Motti Shneor

13

ในฐานะที่กล่าวว่าobjectForKey:ประเภทข้อมูลคือ:(id)aKeyในขณะที่ประเภทข้อมูลคือvalueForKey::(NSString *)key

ตัวอย่างเช่น:

 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. 

ดังนั้นvalueForKey:จะใช้ค่าสตริงเท่านั้นและเป็นวิธี KVC ในขณะที่objectForKey:จะใช้วัตถุประเภทใด

ค่าในobjectForKeyจะสามารถเข้าถึงได้โดยวัตถุชนิดเดียวกัน


0

ฉันจะพยายามให้คำตอบที่ครอบคลุมที่นี่ คะแนนส่วนใหญ่ปรากฏในคำตอบอื่น ๆ แต่ฉันพบว่าคำตอบแต่ละข้อไม่สมบูรณ์และมีบางข้อที่ไม่ถูกต้อง

ก่อนอื่นobjectForKey:เป็นNSDictionaryวิธีการในขณะที่valueForKey:เป็นวิธีโปรโตคอล KVC ที่จำเป็นสำหรับชั้นเรียนร้องเรียน KVC ใด ๆ - รวมถึง NSDictionary

นอกจากนี้ดังที่ @dreamlax เขียนไว้คำแนะนำเอกสารประกอบที่NSDictionaryใช้valueForKey:วิธีการโดยใช้การobjectForKey:ดำเนินการ ในคำอื่น ๆ - เรียกร้องให้[NSDictionary valueForKey:][NSDictionary objectForKey:]

สิ่งนี้บ่งบอกว่าvalueForKey:จะไม่มีทางเร็วไปกว่านี้อีกแล้วobjectForKey: (บนคีย์อินพุตเดียวกัน) แม้ว่าการทดสอบอย่างละเอียดฉันได้บอกถึงความแตกต่างประมาณ 5% ถึง 15% มากกว่าการสุ่มเข้าถึงพันล้าน NSDictionary จำนวนมาก ในสถานการณ์ปกติ - ความแตกต่างนั้นเล็กน้อย

ถัดไป: โปรโตคอล KVC ใช้งานได้กับNSString *คีย์เท่านั้นจึงvalueForKey:จะยอมรับNSString *(หรือคลาสย่อย) เป็นคีย์เท่านั้นในขณะที่NSDictionaryสามารถทำงานกับวัตถุชนิดอื่น ๆ เป็นคีย์ได้เพื่อให้ "ระดับล่าง" objectForKey:ยอมรับวัตถุใด ๆ ที่คัดลอกได้ (NSCopying โปรโตคอลที่สอดคล้องกับโปรโตคอล) เป็นกุญแจสำคัญ

ขั้นสุดท้ายNSDictionary'sการนำการvalueForKey:เบี่ยงเบนจากพฤติกรรมมาตรฐานที่กำหนดไว้ในเอกสารประกอบของ KVC และจะไม่ปล่อยNSUnknownKeyExceptionคีย์ที่ไม่สามารถหาได้เว้นแต่จะเป็นคีย์ "พิเศษ" ซึ่งเริ่มต้นด้วย '@' ซึ่งมักจะหมายถึง " ปุ่มฟังก์ชันการรวมตัว (เช่น@"@sum, @"@avg") แต่จะคืนค่าศูนย์เมื่อไม่พบรหัสในพจนานุกรม NSDictionaryobjectForKey:

ต่อไปนี้เป็นรหัสทดสอบเพื่อแสดงและพิสูจน์บันทึกของฉัน

- (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);
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.