objectForKey和valueForKey之间的区别?


Answers:


403

objectForKey:是一种NSDictionary方法。An NSDictionary是类似于的集合类NSArray,除了不使用索引,它使用键来区分项目。密钥是您提供的任意字符串。任何两个对象都不能具有相同的键(就像一个对象中NSArray不能有相同的索引一样)。

valueForKey:是一种KVC方法。它适用于ANY类。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看起来并没有那么有用。实际上,它看起来很“罗word”。但是,当您想在运行时进行更改时,您可以做很多很酷的事情,而在其他语言中,这要困难得多(但这超出了您的问题范围)。

如果您想了解有关KVC的更多信息,那么如果您有Google的话,可以找到很多教程,特别是在Scott Stevenson的博客上。您也可以签出《NSKeyValueCoding协议参考》

希望能有所帮助。


12
对于NSDictionary对象,valueForKey的行为有所不同,具体取决于键是否以@符号开头。
dreamlax

61
objectForKey:接受任何对象作为键,而不仅仅是字符串。唯一的要求是密钥必须支持NSCopying协议。
阿什莉·克拉克

5
我很惊讶没有人通过指出valueForKey来纠正这个答案:从技术上讲,您不能访问相应的实例变量,而是可以(可以)管理实例变量的访问器方法。
丹妮·乔玛

7
警告:valueForKey可能会非常慢-它目前是我iPad应用程序中的主要瓶颈,它是如此之慢以至于用“标准”词典替换它会使应用程序明显加快。iOS上的KVC出了点问题,我再也不会使用它了-不值得降低性能,而且无论如何都必须对其进行长时间的重新编写。这是在CALayers上使用带有NSString值的NSString键。仪器显示“ CAObject_valueForKey”占总运行时间的25%(!)
亚当

2
@Adam听起来很吓人。自iOS7起,您是否再次尝试过?如果是这样,此后情况是否有所改变?
Unheilig 2014年

64

在执行此操作时,valueForKey:您需要给它一个NSString,而objectForKey:可以将任何NSObject子类作为键。这是因为对于键值编码,键始终是字符串。

实际上,文档指出即使您提供valueForKey:NSString,它也将始终调用objectForKey:,除非字符串以开头@,在这种情况下,它将调用[super valueForKey:],这可能valueForUndefinedKey:会引发异常。


您能给我您所指文件的链接吗?谢谢。
阿里·阿敏

5
@عليامين:就在这里
dreamlax

20

这是一个objectForKey:尽可能使用而不是使用它的重要原因valueForKey:- valueForKey:带有未知密钥会抛出NSUnknownKeyException“该类不符合该密钥的编码值”。


5
知道“ valueForKey:具有未知密钥”将引发NSUnknownKeyException,说“此类不是与密钥兼容的密钥值”
onmyway133

对于NSDictionary来说,这根本不是真的,欢迎您尝试以下方法:NSLog(@“ Z:%@”,[@ {@“ X”:@(10),@“ Y”:@(20)} valueForKey:@“ Z”]); valueForKey将在不支持指定键的其他类上发出此类异常-但是对于NSDictionary子类-您只会收到一个安静的nil。试试这个:
Motti Shneor

13

如前所述,objectForKey:数据类型为,:(id)aKeyvalueForKey:数据类型为:(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投诉类别(包括NSDictionary)都需要KVC协议方法。

此外,正如@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")。相反,当在NSDictionary中找不到密钥时,它将仅返回nil,其行为与objectForKey:

以下是一些测试代码来演示和证明我的笔记。

- (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);
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.