ChatGPT解决这个技术问题 Extra ChatGPT

objectForKey 和 valueForKey 的区别?

objectForKeyvalueForKey 有什么区别?我在文档中查找了两者,它们对我来说似乎相同。


f
fishinear

objectForKey: 是一种 NSDictionary 方法。 NSDictionary 是一个类似于 NSArray 的集合类,不同之处在于它不使用索引,而是使用键来区分项目。密钥是您提供的任意字符串。没有两个对象可以有相同的键(就像 NSArray 中没有两个对象可以有相同的索引)。

valueForKey: 是一种 KVC 方法。它适用于任何课程。 valueForKey: 允许您使用字符串作为其名称来访问属性。例如,如果我有一个具有属性 accountNumberAccount 类,我可以执行以下操作:

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 上特别是在 Scott Stevenson's blog 有很多教程。您还可以查看 NSKeyValueCoding Protocol Reference

希望有帮助。


NSDictionary 对象的 valueForKey 行为不同,具体取决于键是否以 @ 符号开头。
objectForKey:接受任何对象作为键,而不仅仅是字符串。唯一的要求是密钥支持 NSCopying 协议。
我很惊讶没有人通过指出 valueForKey 来纠正这个答案:技术上并没有让您访问相应的实例变量,而是(可以)管理实例变量的访问器方法。
警告: valueForKey 可能非常慢 - 它目前是我 iPad 应用程序的主要瓶颈,速度如此之慢,以至于用“标准”字典替换它会使应用程序明显更快。 iOS 上的 KVC 出了点问题,我再也不会使用它了——不值得降低性能,而且无论如何都要重新编写它。这是在 CALayers 上使用具有 NSString 值的 NSString 键。 Instruments 显示“CAObject_valueForKey”占总运行时间的 25%(!)
@Adam 这听起来很可怕。自iOS7以来您是否再次尝试过?如果是这样,从那以后情况是否发生了变化?
d
dreamlax

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

事实上,文档指出,即使您给 valueForKey: 一个 NSString,它仍然会调用 objectForKey:,除非字符串以 @ 开头,在这种情况下它会调用 [super valueForKey:],它可能会调用 valueForUndefinedKey:可能会引发异常。


你能给我你所指的文档的链接吗?谢谢你。
@عليامين:It's right here
N
Nick Locking

这是尽可能使用 objectForKey: 而不是 valueForKey: 的一个很好的理由 - 带有未知键的 valueForKey: 将抛出 NSUnknownKeyException 说“这个类不符合键的键值编码”。


很高兴知道“valueForKey:使用未知键将抛出 NSUnknownKeyException 说“这个类不符合键的键值编码”
NSDictionary 根本就不是这样,欢迎你试试这个:NSLog(@"Z:%@", [@{@"X":@(10), @"Y":@(20)} valueForKey:@"Z"]); valueForKey 将在其他不支持指定键的类上发出此类异常 - 但对于 NSDictionary 子类 - 你只会收到一个安静的 nil。尝试这个:
t
the Tin Man

如前所述,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 中的值将被同种对象访问。


A
Andy Jazz

此表表示 objectForKeyvalueForKey 之间的四个差异。

objectForKey valueForKey 适用于... NSDictionary NSDictionary / KVC 抛出异常 否 是(在 KVC 上) Feed NSObject 的子类 NSString 在 KVC 上的使用不能


M
Motti Shneor

我将尝试在这里提供一个全面的答案。很多点出现在其他答案中,但我发现每个答案都不完整,有些不正确。

首先,objectForKey: 是一个 NSDictionary 方法,而 valueForKey: 是任何 KVC 投诉类(包括 NSDictionary)所需的 KVC 协议方法。

此外,正如@dreamlax 所写,文档提示 NSDictionary 实现了它的 valueForKey: 方法USING 它的 objectForKey: 实现。换句话说 - [NSDictionary valueForKey:] 调用 [NSDictionary objectForKey:]

这意味着,valueForKey: 永远不会比 objectForKey: 快(在相同的输入键上),尽管我所做的彻底测试暗示了大约 5% 到 15% 的差异,超过数十亿次随机访问一个巨大的 NSDictionary。在正常情况下 - 差异可以忽略不计。

下一篇:KVC 协议仅适用于 NSString * 键,因此 valueForKey: 将只接受 NSString *(或子类)作为键,而 NSDictionary 可以使用其他类型的对象作为键 - 因此“较低级别" objectForKey: 接受任何可复制(符合 NSCopying 协议)对象作为键。

最后,valueForKey:NSDictionary's 实现偏离了 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);
}