2011-04-29 97 views
3

假设我有一个NSDictionary的一个的NSArray和NSDictionary中的两个子集:基础目标-c:带数组的字典;用字典词典

NSMutableDictionary *mkDict(void){ 
    NSMutableDictionary *dict=[NSMutableDictionary dictionary]; 
    NSMutableDictionary *sub=[NSMutableDictionary dictionary]; 
    NSMutableArray *array= [NSMutableArray array]; 
    [dict setObject:array forKey:@"array_key"]; 
    [dict setObject:sub forKey:@"dict_key"]; 
    return dict; 
} 

有很多方法来访问子集的单个元素众多,而且我选择的时间其中三个。

第一种方式是通过访问父的键间接访问子元素:

void KVC1(NSMutableDictionary *dict, int count){ 

    for(int i=0; i<count; i++){ 
     char buf1[40], buf2[sizeof buf1]; 
     snprintf(buf1,sizeof(buf1),"element %i", i); 
     snprintf(buf2, sizeof buf2, "key %i", i); 

     [[dict objectForKey:@"array_key"] 
      addObject: 
      [NSString stringWithUTF8String:buf1]]; 
     [[dict objectForKey:@"dict_key"] 
      setObject:[NSString stringWithUTF8String:buf1] 
      forKey:[NSString stringWithUTF8String:buf2]]; 
    } 
} 

第二个是使用的keyPath访问:

void KVC2(NSMutableDictionary *dict, int count){ 

    for(int i=0; i<count; i++){ 
     char buf1[40], buf2[sizeof buf1], buf3[sizeof buf1]; 
     snprintf(buf1,sizeof(buf1),"element %i", i); 
     snprintf(buf2, sizeof buf2, "key %i", i); 
     snprintf(buf3, sizeof buf3, "dict_key.key %i",i); 

     [dict insertValue: 
      [NSString stringWithUTF8String:buf1] 
      atIndex:i inPropertyWithKey:@"array_key"]; 
     [dict setValue: 
      [NSString stringWithUTF8String:buf1] 
      forKeyPath: 
      [NSString stringWithUTF8String:buf3]]; 
    } 
} 

而第三,类似于首先是访问指向子元素的指针,然后使用该指针:

void KVC3(NSMutableDictionary *dict, int count){ 

    NSMutableArray *subArray = [dict objectForKey:@"array_key"]; 
    NSMutableDictionary *subDict = [dict objectForKey:@"dict_key"]; 

    for(int i=0; i<count; i++){ 
     char buf1[40], buf2[sizeof buf1]; 
     snprintf(buf1,sizeof(buf1),"element %i", i); 
     snprintf(buf2, sizeof buf2, "key %i", i); 

     [subArray addObject:[NSString stringWithUTF8String:buf1]]; 
     [subDict 
      setObject: 
      [NSString stringWithUTF8String:buf1] 
      forKey: 
      [NSString stringWithUTF8String:buf2]]; 
    } 
} 

这里是时刻码:

#import <Foundation/Foundation.h> 
#import <mach/mach_time.h> 

// KVC1, KVC2 and KVC3 from above... 

#define TIME_THIS(func,times) \ 
({\ 
mach_timebase_info_data_t info; \ 
mach_timebase_info(&info); \ 
uint64_t start = mach_absolute_time(); \ 
for(int i=0; i<(int)times; i++) \ 
func ; \ 
uint64_t duration = mach_absolute_time() - start; \ 
duration *= info.numer; \ 
duration /= info.denom; \ 
duration /= 1000000; \ 
NSLog(@"%i executions of line %i took %lld milliseconds", times, __LINE__, duration); \ 
}); 

int main (int argc, const char * argv[]) { 
    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; 
    NSMutableDictionary *dict=mkDict(); 
    NSMutableDictionary *dict2=mkDict(); 
    NSMutableDictionary *dict3=mkDict(); 

    TIME_THIS(KVC1(dict,1000),10); 
    TIME_THIS(KVC2(dict2,1000),10); 
    TIME_THIS(KVC3(dict3,1000),10); 

    if([dict isEqualToDictionary:dict2]) 
     NSLog(@"And they are the same..."); 
    [pool drain]; 
    return 0; 
} 

下面是结果:

10 executions of line 256 took 57 milliseconds 
10 executions of line 257 took 7930 milliseconds 
10 executions of line 258 took 46 milliseconds 
And they are the same... 

问:为什么OS X雪豹/狮子建议使用KeyPaths所以臭慢的方法是什么?如果将count的大小增加到10,000或更多,则KVC2在其他两种方法线性增加的情况下变得无限缓慢。

我做错了什么?访问字典中的子集合的单个元素是否有更好的习惯用法?

回答

3

KVC2()慢,你送

[dict insertValue:[NSString stringWithUTF8String:buf1] 
      atIndex:i 
inPropertyWithKey:@"array_key"]; 

文档以下该方法规定:

方法insertIn<Key>:atIndex:如果存在被调用。如果未找到相应的脚本 - 符合KVC的方法(insertIn<Key>:atIndex:),则此方法调用mutableArrayValueForKey:并改变结果。

由于消息正被发送到dict,的NSDictionary一个实例,没有-insertIn<Key>:atIndex:方法,因此-mutableArrayValueForKey:被发送。此方法的文档指出以下几点:

返回值 一个可变的阵列能提供的键指定有序一对多的关系进行读写访问代理。

讨论 添加到可变数组的对象变得与接收者相关,并且从可变数组中删除的对象变得不相关。默认实现可以识别与valueForKey:相同的简单访问方法和数组访问方法,并遵循相同的直接实例变量访问策略,但始终返回可变集合代理对象,而不是valueForKey:将返回的不可变集合。

所以发生了什么是在每次迭代:

  1. 代理可变数组作为原始阵列的可变副本创建的;
  2. 一个对象被添加到代理数组;
  3. 代理数组将相同的对象添加到原始数组中。

如果用仪器来分析你的程序,你会发现,处理时间约50%是花在-[NSKeyValueSlowMutableArray insertObject:atIndex:] - 我认为它是安全的假设,NSKeyValueSlowMutableArray是代理服务器阵列,它的名字应该是一个其表现的线索。

+0

+1'它的名字应该是它表演的线索。“好笑! :-) – 2011-04-29 19:42:35

1

因为框架需要弄清楚如何获得给定字符串访问路径的元素:解析字符串,错误检查,创建更多字符串实例并释放它们。

这是一个首选的方式,因为键值观察,绑定等,所以可可可以为你制造很多魔法。慢是相对的:它可能比直接访问慢,但速度太慢?只有对真实用例进行分析才能显示它是否太慢,以及是否需要优化。如果你在UI上使用keypath设置了一些变量,那么你可能不会注意到速度的不足,如果你尝试处理大量数据,那么keypath可能不是最好的解决方案。正如我所说:你使用的个人资料。

你必须在易用的代码之间做出妥协,才能访问Cocoa KVC提供的所有好东西,并且只有当你看到代码作为更大图片的一部分时才会知道答案。

1

第1个和第3个实现对子元素有一个“静态”引用,在第3次执行时绝对不会对其进行二次评估。访问子元素的第二个实现的可变性可能导致时间问题... [NSDictionary insertValue:atIndex:forPropertyKey:]在非静态求值元素(NSMutableArray)中存在随机访问插入问题,同时也是KVO程序可能会引起一些未知的副作用...再试一次,而不志愿脚本insertValue:atIndex:forPropertyWithKey:看看的keyPath是缓慢的,我敢打赌,它是比别人,而是在不同的尺度