Sự khác biệt giữa objectForKey và valueForKey?


Câu trả lời:


403

objectForKey:là một NSDictionaryphương pháp. An NSDictionarylà một lớp tập hợp tương tự như một NSArray, ngoại trừ thay vì sử dụng các chỉ mục, nó sử dụng các khóa để phân biệt giữa các mục. Khóa là một chuỗi tùy ý bạn cung cấp. Không có hai đối tượng có thể có cùng khóa (giống như không có hai đối tượng trong mộtNSArray có thể có cùng chỉ mục).

valueForKey:là một phương pháp KVC. Nó hoạt động với bất kỳ lớp nào. valueForKey:cho phép bạn truy cập một thuộc tính bằng cách sử dụng một chuỗi cho tên của nó. Vì vậy, ví dụ, nếu tôi có một Accountlớp với một thuộc tính accountNumber, tôi có thể làm như sau:

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

[newAccount setAccountNumber:anAccountNUmber];

NSNumber *anotherAccountNumber = [newAccount accountNumber];

Sử dụng KVC, tôi có thể truy cập vào tài sản một cách linh hoạt:

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

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

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

Đó là những bộ báo cáo tương đương.

Tôi biết bạn đang nghĩ: wow, nhưng mỉa mai. KVC không có vẻ hữu ích. Trong thực tế, nó trông "dài dòng". Nhưng khi bạn muốn thay đổi mọi thứ trong thời gian chạy, bạn có thể thực hiện nhiều điều thú vị khó khăn hơn nhiều trong các ngôn ngữ khác (nhưng điều này nằm ngoài phạm vi câu hỏi của bạn).

Nếu bạn muốn tìm hiểu thêm về KVC, có rất nhiều hướng dẫn nếu bạn Google đặc biệt là tại blog của Scott Stevenson . Bạn cũng có thể kiểm tra Tham chiếu Giao thức NSKeyValueCoding .

Mong rằng sẽ giúp.


12
valueForKey hoạt động khác nhau đối với các đối tượng NSDipedia tùy thuộc vào việc khóa có bắt đầu bằng ký hiệu @ hay không.
dreamlax

61
objectForKey: chấp nhận bất kỳ đối tượng nào làm khóa, không chỉ các chuỗi. Yêu cầu duy nhất là khóa hỗ trợ giao thức NSCopying.
Ashley Clark

5
Tôi ngạc nhiên không ai sửa câu trả lời này bằng cách chỉ ra valueForKey: về mặt kỹ thuật không cung cấp cho bạn quyền truy cập vào biến đối tượng tương ứng, mà là phương thức truy cập (có thể) quản lý biến thể hiện.
Dany Joumaa

7
Cảnh báo: valueForKey có thể rất chậm - hiện tại nó là một nút cổ chai lớn trong ứng dụng iPad của tôi, chậm đến mức việc thay thế nó bằng từ điển "tiêu chuẩn" khiến ứng dụng nhanh hơn đáng kể. Một cái gì đó rất sai với KVC trên iOS và tôi sẽ không bao giờ sử dụng nó nữa - không đáng để giảm hiệu suất và dù sao cũng phải viết lại nó. Điều này đã sử dụng các khóa NSString với các giá trị NSString trên CALayers. Các công cụ cho thấy "CAObject_valueForKey" là 25% tổng thời gian chạy (!)
Adam

2
@Adam Nghe có vẻ đáng sợ. Bạn đã thử lại từ iOS7 chưa? Nếu vậy, mọi thứ đã thay đổi kể từ đó?
Unheilig

64

Khi bạn valueForKey:cần cung cấp cho nó một NSString, trong khi đó objectForKey:có thể lấy bất kỳ lớp con NSObject nào làm khóa. Điều này là do mã hóa khóa-giá trị, các khóa luôn là các chuỗi.

Trong thực tế, tài liệu nói rằng ngay cả khi bạn cung cấp valueForKey:NSString, nó sẽ gọi bằng objectForKey:mọi cách trừ khi chuỗi bắt đầu bằng một @, trong trường hợp nó gọi [super valueForKey:], có thể gọi valueForUndefinedKey:có thể đưa ra ngoại lệ.


bạn có thể vui lòng cho tôi liên kết của tài liệu bạn muốn nói cảm ơn bạn.
Ali Amin

5
@ عليامين: Nó ở ngay đây
dreamlax

20

Đây là một lý do tuyệt vời để sử dụng objectForKey:bất cứ nơi nào có thể thay vì valueForKey:- valueForKey:với một khóa không xác định sẽ NSUnknownKeyExceptionnói rằng "lớp này không phải là giá trị khóa tuân thủ mã hóa cho khóa".


5
thật tốt khi biết rằng "valueForKey: với một khóa không xác định sẽ ném NSUn UnknownKeyException nói rằng" lớp này không phải là khóa giá trị tuân thủ mã hóa cho khóa "
onmyway133

Điều này chỉ đơn giản là không đúng với NSDipedia và bạn được chào đón dùng thử: NSLog (@ "Z:% @", [@ {@ "X": @ (10), @ "Y": @ (20)} valueForKey: @ "Z"]); valueForKey sẽ phát ra các ngoại lệ như vậy trên các lớp khác không hỗ trợ khóa được chỉ định - nhưng đối với các lớp con NSDipedia - bạn sẽ chỉ nhận được một con số không. thử cái này:
Motti Shneor 04/07/19

13

Như đã nói, objectForKey:kiểu dữ liệu :(id)aKeytrong khi valueForKey:kiểu dữ liệu là:(NSString *)key .

Ví dụ:

 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. 

Vì vậy, valueForKey:sẽ chỉ lấy một giá trị chuỗi và là phương thức KVC, trong khi đóobjectForKey: sẽ lấy bất kỳ loại đối tượng nào.

Giá trị trong objectForKeysẽ được truy cập bởi cùng loại đối tượng.


0

Tôi sẽ cố gắng cung cấp một câu trả lời toàn diện ở đây. Phần lớn các điểm xuất hiện trong các câu trả lời khác, nhưng tôi thấy mỗi câu trả lời không đầy đủ, và một số điểm không chính xác.

Đầu tiên và quan trọng nhất, objectForKey:là một NSDictionaryphương pháp, trong khivalueForKey: là một phương thức giao thức KVC cần có của bất kỳ lớp khiếu nại KVC nào - bao gồm NSDipedia.

Hơn nữa, như @dreamlax đã viết, tài liệu gợi ý NSDictionarythực hiện valueForKey:phương pháp SỬ DỤNG việc objectForKey:thực hiện nó . Nói cách khác - [NSDictionary valueForKey:]gọi vào[NSDictionary objectForKey:] .

Điều này ngụ ý, điều đó valueForKey:không bao giờ có thể nhanh hơnobjectForKey: (trên cùng một khóa đầu vào) mặc dù thử nghiệm kỹ lưỡng tôi đã thực hiện ngụ ý chênh lệch khoảng 5% đến 15%, qua hàng tỷ truy cập ngẫu nhiên vào một NSDipedia khổng lồ. Trong tình huống bình thường - sự khác biệt là không đáng kể.

Tiếp theo: Giao thức KVC chỉ hoạt động với NSString *các khóa, do đó valueForKey:sẽ chỉ chấp nhận một NSString *(hoặc lớp con) làm khóa, trong khi NSDictionarycó thể hoạt động với các loại đối tượng khác làm khóa - để "mức thấp hơn" objectForKey:chấp nhận mọi đối tượng có thể sao chép (tuân thủ giao thức NSCopying) làm chìa khóa

Cuối cùng, NSDictionary'sviệc thực hiện các sai valueForKey:lệch so với hành vi tiêu chuẩn được xác định trong tài liệu của KVC và sẽ KHÔNG phát ra NSUnknownKeyExceptionkhóa mà nó không thể tìm thấy - trừ khi đây là khóa "đặc biệt" - bắt đầu bằng '@' - thường có nghĩa là " tập hợp "phím chức năng (ví dụ @"@sum, @"@avg"). Thay vào đó, nó sẽ chỉ trả về một con số không khi không tìm thấy khóa trong NSDipedia - hoạt động giống nhưobjectForKey:

Sau đây là một số mã kiểm tra để chứng minh và chứng minh ghi chú của tôi.

- (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);
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.