2011-04-29 76 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是緩慢的,我敢打賭,它是比別人,而是在不同的尺度