nsdictionary - Foundation Objective-c: Dictionary with array; dict with dict -


suppose have nsdictionary 2 sub collections of nsarray , nsdictionary:

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; } 

there multitude of ways access single element of sub-collection, , choose time 3 of them.

the first way indirectly access subelements accessing key of parent:

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]];     } } 

the second use keypath access:

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]];     } } 

and third, similar first, access pointer sub element, use pointer:

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]];     } } 

here timing code:

#import <foundation/foundation.h> #import <mach/mach_time.h>  // kvc1, kvc2 , kvc3 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 same...");     [pool drain];     return 0; } 

here results:

10 executions of line 256 took 57 milliseconds 10 executions of line 257 took 7930 milliseconds 10 executions of line 258 took 46 milliseconds , same... 

question: why os x snow leopard / lion suggested method of using keypaths stinking slow? if increase size of count 10,000 or more, kvc2 becomes infinitely slow other 2 methods increase linearly.

am doing wrong? there better idiom access single element of sub collection in dictionary?

in kvc2(), send

[dict insertvalue:[nsstring stringwithutf8string:buf1]            atindex:i inpropertywithkey:@"array_key"];  

the documentation method states following:

the method insertin<key>:atindex: invoked if exists. if no corresponding scripting-kvc-compliant method (insertin<key>:atindex:) found, method invokes mutablearrayvalueforkey: , mutates result.

since message being sent dict, instance of nsdictionary, there no -insertin<key>:atindex: method, hence -mutablearrayvalueforkey: sent. documentation method states following:

return value mutable array proxy provides read-write access ordered to-many relationship specified key.

discussion objects added mutable array become related receiver, , objects removed mutable array become unrelated. default implementation recognizes same simple accessor methods , array accessor methods valueforkey:, , follows same direct instance variable access policies, returns mutable collection proxy object instead of immutable collection valueforkey: return.

so what’s happening at each iteration:

  1. a proxy mutable array created mutable copy of original array;
  2. an object added proxy array;
  3. the proxy array adds same object original array.

if use instruments profile program, you’ll notice 50% of processing time spent on -[nskeyvalueslowmutablearray insertobject:atindex:] — think it’s safe assume nskeyvalueslowmutablearray proxy array , name should clue of performance.


Comments