/ NSDictionary.m
NSDictionary.m
1 // 2 // NSDictionary.m 3 // CoreFoundation 4 // 5 // Copyright (c) 2014 Apportable. All rights reserved. 6 // 7 8 #import <Foundation/NSDictionary.h> 9 10 #import <CoreFoundation/CFData.h> 11 #import <CoreFoundation/CFPropertyList.h> 12 #import <Foundation/NSArray.h> 13 #import <Foundation/NSSet.h> 14 #import <Foundation/NSURL.h> 15 #include <Foundation/NSError.h> 16 17 #import "CFInternal.h" 18 #import "CFSortFunctions.h" 19 #import "NSFastEnumerationEnumerator.h" 20 #import <Foundation/NSKeyValueObserving.h> 21 #import "NSObjectInternal.h" 22 #import "NSStringInternal.h" 23 24 CF_EXPORT Boolean _CFDictionaryIsMutable(CFDictionaryRef ref); 25 CF_EXPORT void _CFDictionarySetKVOBit(CFDictionaryRef hc, CFIndex bit); 26 CF_EXPORT NSUInteger _CFDictionaryFastEnumeration(CFDictionaryRef hc, NSFastEnumerationState *state, id __unsafe_unretained stackbuffer[], NSUInteger count); 27 CF_EXPORT CFDataRef _CFPropertyListCreateXMLData(CFAllocatorRef allocator, CFPropertyListRef propertyList, Boolean checkValidPlist); 28 29 @interface NSDictionary (Internal) 30 + (id)newWithContentsOf:(id)source immutable:(BOOL)immutable; 31 @end 32 33 CF_PRIVATE 34 @interface __NSPlaceholderDictionary : NSMutableDictionary 35 + (id)mutablePlaceholder; 36 + (id)immutablePlaceholder; 37 @end 38 39 CF_PRIVATE 40 @interface __NSDictionaryObjectEnumerator : __NSFastEnumerationEnumerator 41 @end 42 43 CF_PRIVATE 44 @interface __NSCFDictionary : NSMutableDictionary { 45 unsigned char _cfinfo[4]; 46 unsigned int _bits[4]; 47 void *_callbacks; 48 id *_values; 49 id *_keys; 50 } 51 @end 52 53 CF_PRIVATE 54 @interface __NSDictionaryI : NSDictionary { 55 unsigned int _used:26; 56 unsigned int _szidx:6; 57 } 58 @end 59 60 @implementation __NSDictionaryObjectEnumerator : __NSFastEnumerationEnumerator 61 62 - (id)nextObject 63 { 64 return [(NSDictionary *)_obj objectForKey:[super nextObject]]; 65 } 66 67 @end 68 69 @implementation NSDictionary 70 71 + (id)allocWithZone:(NSZone *)zone 72 { 73 NSDictionary *dictionary = nil; 74 75 if (self == [NSDictionary class]) 76 { 77 dictionary = [__NSPlaceholderDictionary immutablePlaceholder]; 78 } 79 else if (self == [NSMutableDictionary class] || self == [__NSCFDictionary class]) 80 { 81 // we check for `[__NSCFDictionary class]` because Xcode has a convenience class method defined on NSMutableDictionary 82 // that returns `self` as the mutable version of the class (as it should). however, since our `__NSCFDictionary` inherits 83 // from NSMutableDictionary, it also inherits this cateogry class method. `__NSCFDictionary` itself can be either, 84 // but we err on the side of caution and return a mutable placeholder. 85 dictionary = [__NSPlaceholderDictionary mutablePlaceholder]; 86 } 87 else 88 { 89 dictionary = [super allocWithZone:zone]; 90 } 91 92 return dictionary; 93 } 94 95 - (NSUInteger)count 96 { 97 NSRequestConcreteImplementation(); 98 return 0; 99 } 100 101 - (id)objectForKey:(id)aKey 102 { 103 NSRequestConcreteImplementation(); 104 return nil; 105 } 106 107 - (NSEnumerator *)keyEnumerator 108 { 109 NSRequestConcreteImplementation(); 110 return nil; 111 } 112 113 - (NSEnumerator *)objectEnumerator 114 { 115 return [[[__NSDictionaryObjectEnumerator alloc] initWithObject:self] autorelease]; 116 } 117 118 - (id)copyWithZone:(NSZone *)zone 119 { 120 return [[NSDictionary allocWithZone:zone] initWithDictionary:self copyItems:NO]; 121 } 122 123 - (id)mutableCopyWithZone:(NSZone *)zone 124 { 125 return [[NSMutableDictionary allocWithZone:zone] initWithDictionary:self copyItems:NO]; 126 } 127 128 + (BOOL)supportsSecureCoding 129 { 130 return YES; 131 } 132 133 - (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state objects:(id __unsafe_unretained [])buffer count:(NSUInteger)len 134 { 135 // this is technically incorrect and should be refactored to use [self keyEnumerator] instead and manage it's own state 136 return [[self allKeys] countByEnumeratingWithState:state objects:buffer count:len]; 137 } 138 139 - (CFTypeID)_cfTypeID 140 { 141 return CFDictionaryGetTypeID(); 142 } 143 144 - (BOOL)isNSDictionary__ 145 { 146 return YES; 147 } 148 149 @end 150 151 @implementation NSMutableDictionary 152 153 + (id)dictionaryWithCapacity:(NSUInteger)numItems 154 { 155 return [[(NSMutableDictionary *)[self alloc] initWithCapacity:numItems] autorelease]; 156 } 157 158 - (id)initWithObjects:(const id [])objects forKeys:(const id <NSCopying> [])keys count:(NSUInteger)cnt 159 { 160 if ((self = [self initWithCapacity:cnt])) 161 { 162 for (NSUInteger i=0; i<cnt; ++i) 163 { 164 [self setObject:objects[i] forKey:keys[i]]; 165 } 166 } 167 return self; 168 } 169 170 - (id)initWithCapacity:(NSUInteger)capacity 171 { 172 NSRequestConcreteImplementation(); 173 [self release]; 174 return nil; 175 } 176 177 - (void)removeObjectForKey:(id)aKey 178 { 179 NSRequestConcreteImplementation(); 180 } 181 182 - (void)setObject:(id)anObject forKey:(id <NSCopying>)aKey 183 { 184 NSRequestConcreteImplementation(); 185 } 186 187 @end 188 189 static CFDictionaryKeyCallBacks sNSCFDictionaryKeyCallBacks = { 190 0, 191 &_NSCFRetain2, 192 &_NSCFRelease2, 193 &_NSCFCopyDescription, 194 &_NSCFEqual, 195 &_NSCFHash 196 }; 197 198 static CFDictionaryValueCallBacks sNSCFDictionaryValueCallBacks = { 199 0, 200 &_NSCFRetain2, 201 &_NSCFRelease2, 202 &_NSCFCopyDescription, 203 &_NSCFEqual 204 }; 205 206 static __NSPlaceholderDictionary *mutablePlaceholder = nil; 207 static __NSPlaceholderDictionary *immutablePlaceholder = nil; 208 209 @implementation __NSPlaceholderDictionary 210 211 + (void)initialize 212 { 213 214 } 215 216 - (void)removeObjectForKey:(id)key 217 { 218 NSRequestConcreteImplementation(); 219 } 220 221 - (void)setObject:(id)obj forKey:(id)key 222 { 223 NSRequestConcreteImplementation(); 224 } 225 226 - (NSEnumerator *)keyEnumerator 227 { 228 NSRequestConcreteImplementation(); 229 return nil; 230 } 231 232 - (id)objectForKey:(id)key 233 { 234 NSRequestConcreteImplementation(); 235 return nil; 236 } 237 238 - (NSUInteger)count 239 { 240 NSRequestConcreteImplementation(); 241 return 0; 242 } 243 244 + (id)immutablePlaceholder 245 { 246 static dispatch_once_t once = 0L; 247 dispatch_once(&once, ^{ 248 immutablePlaceholder = [__NSPlaceholderDictionary allocWithZone:nil]; 249 }); 250 return immutablePlaceholder; 251 } 252 253 + (id)mutablePlaceholder 254 { 255 static dispatch_once_t once = 0L; 256 dispatch_once(&once, ^{ 257 mutablePlaceholder = [__NSPlaceholderDictionary allocWithZone:nil]; 258 }); 259 return mutablePlaceholder; 260 } 261 262 SINGLETON_RR() 263 264 - (id)initWithContentsOfURL:(NSURL *)url 265 { 266 if (url != nil && [url isNSString__]) 267 { 268 @throw [NSException exceptionWithName:NSInvalidArgumentException reason:@"sent NSString to initWithContentsOfURL - please send a URL" userInfo:nil]; 269 return nil; 270 } 271 272 return (id)[NSDictionary newWithContentsOf:url immutable:(self == immutablePlaceholder)]; 273 } 274 275 - (id)initWithContentsOfFile:(NSString *)path 276 { 277 return (id)[NSDictionary newWithContentsOf:path immutable:(self == immutablePlaceholder)]; 278 } 279 280 - (id)init 281 { 282 if (self == mutablePlaceholder) 283 { 284 return [self initWithCapacity:0]; 285 } 286 else 287 { 288 return [self initWithObjects:NULL forKeys:NULL count:0]; 289 } 290 } 291 292 - (id)initWithCapacity:(NSUInteger)capacity 293 { 294 if (self == mutablePlaceholder) 295 { 296 NSCapacityCheck(capacity, 0x40000000, @"Please rethink the size of the capacity of the dictionary you are creating: %d seems a bit exessive", capacity); 297 return (id)CFDictionaryCreateMutable(kCFAllocatorDefault, capacity, &sNSCFDictionaryKeyCallBacks, &sNSCFDictionaryValueCallBacks); 298 } 299 else 300 { 301 [self doesNotRecognizeSelector:_cmd]; 302 return nil; 303 } 304 } 305 306 - (id)initWithObjects:(const id [])objects forKeys:(const id <NSCopying> [])keys count:(NSUInteger)cnt 307 { 308 if (cnt != 0) 309 { 310 if (objects == NULL) 311 { 312 [self release]; 313 [NSException raise:NSInvalidArgumentException format:@"Tried to init dictionary with nonzero count but NULL object array"]; 314 return nil; 315 } 316 if (keys == NULL) 317 { 318 [self release]; 319 [NSException raise:NSInvalidArgumentException format:@"Tried to init dictionary with nonzero count but NULL key array"]; 320 return nil; 321 } 322 } 323 324 for (NSUInteger idx = 0; idx < cnt; idx++) 325 { 326 if (objects[idx] == nil) 327 { 328 [self release]; 329 [NSException raise:NSInvalidArgumentException format:@"Tried to init dictionary with nil object"]; 330 return nil; 331 } 332 if (keys[idx] == nil) 333 { 334 [self release]; 335 [NSException raise:NSInvalidArgumentException format:@"Tried to init dictionary with nil key"]; 336 return nil; 337 } 338 } 339 340 if (self == mutablePlaceholder) 341 { 342 CFMutableDictionaryRef dict = CFDictionaryCreateMutable(kCFAllocatorDefault, cnt, &sNSCFDictionaryKeyCallBacks, &sNSCFDictionaryValueCallBacks); 343 for (NSUInteger idx = 0; idx < cnt; idx++) 344 { 345 if ([(NSObject *)keys[idx] respondsToSelector:@selector(copyWithZone:)]) 346 { 347 CFDictionarySetValue(dict, keys[idx], objects[idx]); 348 } 349 else 350 { 351 CFRelease(dict); 352 dict = NULL; 353 @throw [NSException exceptionWithName:NSInvalidArgumentException reason:@"" userInfo:@{}]; 354 } 355 } 356 357 return (id)dict; 358 } 359 else 360 { 361 return (id)CFDictionaryCreate(kCFAllocatorDefault, (const void **)keys, (const void **)objects, cnt, &sNSCFDictionaryKeyCallBacks, &sNSCFDictionaryValueCallBacks); 362 } 363 } 364 365 @end 366 367 @implementation NSDictionary (NSDictionaryCreation) 368 369 + (id)dictionary 370 { 371 return [[[self alloc] initWithObjects:NULL forKeys:NULL count:0] autorelease]; 372 } 373 374 + (id)dictionaryWithObject:(id)object forKey:(id <NSCopying>)key 375 { 376 return [[[self alloc] initWithObjects:&object forKeys:&key count:1] autorelease]; 377 } 378 379 + (id)dictionaryWithObjects:(const id [])objects forKeys:(const id <NSCopying> [])keys count:(NSUInteger)count 380 { 381 return [[[self alloc] initWithObjects:objects forKeys:keys count:count] autorelease]; 382 } 383 384 + (id)dictionaryWithObjectsAndKeys:(id)firstObject, ... 385 { 386 va_list args; 387 va_start(args, firstObject); 388 id value = firstObject; 389 id key = va_arg(args, id); 390 size_t size = 32; 391 size_t count = 0; 392 id *objects = malloc(sizeof(id) * size); 393 394 if (UNLIKELY(objects == NULL)) 395 { 396 return nil; 397 } 398 399 id *keys = malloc(sizeof(id) * size); 400 401 if (UNLIKELY(keys == NULL)) 402 { 403 free(objects); 404 return nil; 405 } 406 407 while(value != NULL && key != NULL) 408 { 409 count++; 410 411 if (count > size) 412 { 413 size += 32; 414 objects = (id *)realloc(objects, sizeof(id) * size); 415 416 if (UNLIKELY(objects == NULL)) 417 { 418 free(keys); 419 return nil; 420 } 421 422 keys = (id *)realloc(keys, sizeof(id) * size); 423 424 if (UNLIKELY(keys == NULL)) 425 { 426 free(objects); 427 return nil; 428 } 429 } 430 objects[count - 1] = value; 431 keys[count - 1] = key; 432 value = va_arg(args, id); 433 434 if (value == nil) 435 { 436 break; 437 } 438 439 key = va_arg(args, id); 440 } 441 442 id dict = [[self alloc] initWithObjects:objects forKeys:keys count:count]; 443 free(objects); 444 free(keys); 445 return [dict autorelease]; 446 } 447 448 + (id)dictionaryWithDictionary:(NSDictionary *)dict 449 { 450 size_t count = [dict count]; 451 id *objects = NULL; 452 id *keys = NULL; 453 454 if (count > 0) 455 { 456 objects = malloc(sizeof(id) * count); 457 458 if (UNLIKELY(objects == NULL)) 459 { 460 return NULL; 461 } 462 463 keys = malloc(sizeof(id) * count); 464 465 if (UNLIKELY(keys == NULL)) 466 { 467 free(objects); 468 return NULL; 469 } 470 } 471 [dict getObjects:objects andKeys:keys]; 472 id obj = [[self alloc] initWithObjects:objects forKeys:keys count:count]; 473 474 if (objects != NULL) 475 { 476 free(objects); 477 } 478 479 if (keys != NULL) 480 { 481 free(keys); 482 } 483 484 return [obj autorelease]; 485 } 486 487 + (id)dictionaryWithObjects:(NSArray *)objectArray forKeys:(NSArray *)keyArray 488 { 489 return [[[self alloc] initWithObjects:objectArray forKeys:keyArray] autorelease]; 490 } 491 492 + (id)dictionaryWithContentsOfFile:(NSString *)path 493 { 494 if (path == nil) 495 { 496 return nil; 497 } 498 499 return [[[self alloc] initWithContentsOfURL:[NSURL fileURLWithPath:path]] autorelease]; 500 } 501 502 + (id)dictionaryWithContentsOfURL:(NSURL *)url 503 { 504 return [[[self alloc] initWithContentsOfURL:url] autorelease]; 505 } 506 507 + (instancetype)dictionaryWithContentsOfURL:(NSURL *)url error:(NSError **)error 508 { 509 // TODO: we should actually pass along any error we get to the user so they have more info 510 // this is just a quick and dirty implementation 511 NSDictionary* tmp = [self dictionaryWithContentsOfURL:url]; 512 if (tmp == nil) { 513 *error = [NSError errorWithDomain:@"NSDictionaryErrorDomain" code:-1 userInfo:nil]; 514 } 515 return tmp; 516 } 517 518 - (id)initWithObjectsAndKeys:(id)firstObject, ... 519 { 520 va_list args; 521 va_start(args, firstObject); 522 id value = firstObject; 523 id key = va_arg(args, id); 524 size_t size = 32; 525 size_t count = 0; 526 id *values = (id *)malloc(sizeof(id) * size); 527 528 if (UNLIKELY(values == NULL)) 529 { 530 [self release]; 531 return nil; 532 } 533 534 id *keys = (id *)malloc(sizeof(id) * size); 535 536 if (UNLIKELY(keys == NULL)) 537 { 538 free(values); 539 [self release]; 540 return nil; 541 } 542 543 while(value != NULL && key != NULL) 544 { 545 count++; 546 547 if (count > size) 548 { 549 size += 32; 550 values = (id *)realloc(values, sizeof(id) * size); 551 552 if (UNLIKELY(values == NULL)) 553 { 554 free(keys); 555 [self release]; 556 return nil; 557 } 558 559 keys = (id *)realloc(keys, sizeof(id) * size); 560 if (UNLIKELY(keys == NULL)) 561 { 562 free(values); 563 [self release]; 564 return nil; 565 } 566 } 567 values[count - 1] = value; 568 keys[count - 1] = key; 569 value = va_arg(args, id); 570 key = va_arg(args, id); 571 } 572 573 id dict = [self initWithObjects:values forKeys:keys count:count]; 574 free(values); 575 free(keys); 576 return dict; 577 } 578 579 - (id)initWithDictionary:(NSDictionary *)otherDictionary 580 { 581 size_t count = [otherDictionary count]; 582 id *objects = NULL; 583 id *keys = NULL; 584 585 if (count > 0) 586 { 587 objects = malloc(sizeof(id) * count); 588 589 if (UNLIKELY(objects == NULL)) 590 { 591 [self release]; 592 return NULL; 593 } 594 595 keys = malloc(sizeof(id) * count); 596 597 if (UNLIKELY(keys == NULL)) 598 { 599 [self release]; 600 free(objects); 601 return NULL; 602 } 603 } 604 [otherDictionary getObjects:objects andKeys:keys]; 605 id dict = [self initWithObjects:objects forKeys:keys count:count]; 606 607 if (objects != NULL) 608 { 609 free(objects); 610 } 611 612 if (keys != NULL) 613 { 614 free(keys); 615 } 616 617 return dict; 618 } 619 620 - (id)initWithDictionary:(NSDictionary *)otherDictionary copyItems:(BOOL)flag 621 { 622 size_t count = [otherDictionary count]; 623 id *objects = NULL; 624 id *keys = NULL; 625 626 if (count > 0) 627 { 628 objects = malloc(sizeof(id) * count); 629 630 if (UNLIKELY(objects == NULL)) 631 { 632 [self release]; 633 return NULL; 634 } 635 636 keys = malloc(sizeof(id) * count); 637 638 if (UNLIKELY(keys == NULL)) 639 { 640 free(objects); 641 [self release]; 642 return NULL; 643 } 644 } 645 646 [otherDictionary getObjects:objects andKeys:keys]; 647 648 for (int i = 0 ; i < count; i++) 649 { 650 if (flag && [objects[i] respondsToSelector:@selector(copyWithZone:)]) 651 { 652 objects[i] = [objects[i] copy]; 653 } 654 else 655 { 656 objects[i] = [objects[i] retain]; 657 } 658 } 659 660 id dict = [self initWithObjects:objects forKeys:keys count:count]; 661 662 if (objects != NULL) 663 { 664 free(objects); 665 } 666 667 if (keys != NULL) 668 { 669 free(keys); 670 } 671 672 return dict; 673 } 674 675 - (id)initWithObjects:(NSArray *)objectArray forKeys:(NSArray *)keyArray 676 { 677 NSUInteger objectCount = [objectArray count]; 678 NSUInteger keyCount = [keyArray count]; 679 680 if (objectCount != keyCount) 681 { 682 [NSException raise:NSInvalidArgumentException format:@"Object and key arrays have different number of elements (%d and %d)", objectCount, keyCount]; 683 return nil; 684 } 685 686 id *objects = malloc(sizeof(id) * objectCount); 687 688 if (UNLIKELY(objects == NULL)) 689 { 690 [self release]; 691 return nil; 692 } 693 694 id *keys = malloc(sizeof(id) * keyCount); 695 696 if (UNLIKELY(keys == NULL)) 697 { 698 [self release]; 699 free(objects); 700 return nil; 701 } 702 703 [objectArray getObjects:objects range:NSMakeRange(0, objectCount)]; 704 [keyArray getObjects:keys range:NSMakeRange(0, keyCount)]; 705 id dict = [self initWithObjects:objects forKeys:keys count:objectCount]; 706 free(objects); 707 free(keys); 708 return dict; 709 } 710 711 - (instancetype)initWithObjects:(const id [])objects forKeys:(const id <NSCopying> [])keys count:(NSUInteger)cnt 712 { 713 NSRequestConcreteImplementation(); 714 [self release]; 715 return nil; 716 } 717 718 - (id)initWithContentsOfFile:(NSString *)path 719 { 720 BOOL immutable = ![self isKindOfClass:[NSMutableDictionary class]]; 721 [self release]; 722 return [NSDictionary newWithContentsOf:path immutable:immutable]; 723 } 724 725 - (id)initWithContentsOfURL:(NSURL *)url 726 { 727 BOOL immutable = ![self isKindOfClass:[NSMutableDictionary class]]; 728 [self release]; 729 return [NSDictionary newWithContentsOf:url immutable:immutable]; 730 } 731 732 - (id)initWithContentsOfURL:(NSURL *)url error:(NSError **)error 733 { 734 NSDictionary* tmp = [self initWithContentsOfURL:url]; 735 if (tmp == nil) { 736 *error = [NSError errorWithDomain:@"NSDictionaryErrorDomain" code:-1 userInfo:nil]; 737 } 738 return tmp; 739 } 740 741 @end 742 743 744 @implementation NSDictionary (NSExtendedDictionary) 745 746 - (void)__apply:(void (*)(const void *, const void *, void *))applier context:(void *)context 747 { 748 for (id<NSCopying> key in self) 749 { 750 id value = [self objectForKey:key]; 751 applier(key, value, context); 752 } 753 } 754 755 - (NSArray *)allKeys 756 { 757 NSUInteger count = [self count]; 758 NSMutableArray *keys = [[NSMutableArray alloc] initWithCapacity:count]; 759 NSEnumerator *enumerator = [self keyEnumerator]; 760 id key = nil; 761 762 while (key = [enumerator nextObject]) 763 { 764 [keys addObject:key]; 765 } 766 767 NSArray *retval = [keys copy]; 768 [keys release]; 769 return [retval autorelease]; 770 } 771 772 - (NSArray *)allKeysForObject:(id)anObject 773 { 774 NSUInteger count = [self count]; 775 NSMutableArray *keys = [[NSMutableArray alloc] initWithCapacity:count]; 776 NSEnumerator *enumerator = [self keyEnumerator]; 777 id key = nil; 778 779 while (key = [enumerator nextObject]) 780 { 781 if ([[self objectForKey:key] isEqual:anObject]) 782 { 783 [keys addObject:key]; 784 } 785 } 786 787 NSArray *retval = [keys copy]; 788 [keys release]; 789 return [retval autorelease]; 790 } 791 792 - (NSArray *)allValues 793 { 794 NSArray *keys = [self allKeys]; 795 NSMutableArray *values = [[NSMutableArray alloc] initWithCapacity:[keys count]]; 796 797 for (id key in keys) 798 { 799 id value = [self objectForKey:key]; 800 [values addObject:value]; 801 } 802 803 NSArray *retval = [values copy]; 804 [values release]; 805 return [retval autorelease]; 806 } 807 808 - (NSString *)description 809 { 810 return [self descriptionWithLocale:nil indent:0]; 811 } 812 813 - (NSString *)descriptionWithLocale:(id)locale 814 { 815 return [self descriptionWithLocale:locale indent:0]; 816 } 817 818 static NSString *_getDescription(id obj, id locale, int level) 819 { 820 if ([obj isKindOfClass:[NSDictionary class]] || [obj isKindOfClass:[NSArray class]]) 821 { 822 return [obj descriptionWithLocale:locale indent:level + 1]; 823 } 824 else if ([obj respondsToSelector:@selector(descriptionWithLocale:)]) 825 { 826 return [obj descriptionWithLocale:locale]; 827 } 828 else 829 { 830 return [obj description]; 831 } 832 } 833 834 - (NSString *)descriptionWithLocale:(id)locale indent:(NSUInteger)level 835 { 836 CFMutableStringRef description = CFStringCreateMutable(kCFAllocatorDefault, 0); 837 CFStringAppendFormat(description, NULL, CFSTR("%*s{\n"), (int)level * strlen(INDENT), ""); 838 int count = [self count]; 839 id *keys = (id *)malloc(sizeof(id) * count); 840 [self getObjects:nil andKeys:keys]; 841 842 Class checkKeyClass = nil; 843 844 for (int i = 0; i < count; i++) 845 { 846 if (checkKeyClass == nil) 847 { 848 checkKeyClass = object_getClass(keys[i]); 849 } 850 else if (checkKeyClass != object_getClass(keys[i])) 851 { 852 checkKeyClass = nil; 853 break; 854 } 855 } 856 857 if (checkKeyClass && [keys[0] respondsToSelector: @selector(compare:)]) // homogeneous, sortable NSDictionary's should be sorted 858 { 859 CFIndex *indexes = (CFIndex *)malloc(count * sizeof(CFIndex)); 860 CFSortIndexes(indexes, count, 0, ^(CFIndex i1, CFIndex i2) { 861 return (CFComparisonResult)[keys[i1] compare: keys[i2]]; 862 }); 863 864 for (int i = 0; i < count; i++) 865 { 866 id key = keys[indexes[i]]; 867 id value = [self objectForKey:key]; 868 NSString *valueDescription = _getDescription(value, locale, level); 869 NSString *keyDescription = _getDescription(key, locale, level); 870 CFStringAppendFormat(description, NULL, CFSTR("%*s%@ = %@;\n"), ((int)level + 1) * strlen(INDENT), "", keyDescription, valueDescription); 871 } 872 873 free(indexes); 874 } 875 else { 876 NSEnumerator *enumerator = [self keyEnumerator]; 877 878 for (id key = [enumerator nextObject]; key != nil; key = [enumerator nextObject]) 879 { 880 id value = [self objectForKey:key]; 881 NSString *valueDescription = _getDescription(value, locale, level); 882 NSString *keyDescription = _getDescription(key, locale, level); 883 CFStringAppendFormat(description, NULL, CFSTR("%*s%@ = %@;\n"), ((int)level + 1) * strlen(INDENT), "", keyDescription, valueDescription); 884 } 885 } 886 CFStringAppendFormat(description, NULL, CFSTR("%*s}"), (int)level * strlen(INDENT), ""); 887 CFStringRef desc = CFStringCreateCopy(kCFAllocatorDefault, description); 888 CFRelease(description); 889 free(keys); 890 return [(NSString *)desc autorelease]; 891 } 892 893 - (NSUInteger)hash 894 { 895 return [self count]; 896 } 897 898 - (BOOL)isEqual:(id)other 899 { 900 if (![other isNSDictionary__]) 901 { 902 return NO; 903 } 904 905 return [self isEqualToDictionary:other]; 906 } 907 908 - (BOOL)isEqualToDictionary:(NSDictionary *)otherDictionary 909 { 910 if (self == otherDictionary) 911 { 912 return YES; 913 } 914 915 if (![otherDictionary isKindOfClass:[NSDictionary class]]) 916 { 917 // @throw [NSException exceptionWithName:NSInvalidArgumentException reason:@"Second argument is not a dictionary" userInfo:nil]; 918 return NO; 919 } 920 921 if ([self count] != [otherDictionary count]) 922 { 923 return NO; 924 } 925 926 for (NSString* key in [self allKeys]) 927 { 928 id val = [self objectForKey:key]; 929 id otherVal = [otherDictionary objectForKey:key]; 930 if (val == otherVal) 931 { 932 continue; 933 } 934 935 if (![val isEqual:otherVal]) 936 { 937 return NO; 938 } 939 } 940 941 return YES; 942 } 943 944 - (NSEnumerator *)objectEnumerator 945 { 946 return [[self allValues] objectEnumerator]; 947 } 948 949 - (NSArray *)objectsForKeys:(NSArray *)keys notFoundMarker:(id)marker 950 { 951 NSMutableArray *objects = [[NSMutableArray alloc] initWithCapacity:[keys count]]; 952 953 for (id key in keys) 954 { 955 id obj = [self objectForKey:key]; 956 957 if (obj == nil) 958 { 959 obj = marker; 960 } 961 962 if (obj != nil) 963 { 964 [objects addObject:obj]; 965 } 966 } 967 968 NSArray *objs = [objects copy]; 969 [objects release]; 970 return [objs autorelease]; 971 } 972 973 - (BOOL)writeToFile:(NSString *)path atomically:(BOOL)atomically 974 { 975 NSData *data = (NSData *)_CFPropertyListCreateXMLData(kCFAllocatorDefault, (CFPropertyListRef)self, true); 976 BOOL success = [data writeToFile:path atomically:atomically]; 977 [data release]; 978 return success; 979 } 980 981 - (BOOL)writeToURL:(NSURL *)url atomically:(BOOL)atomically 982 { 983 NSData *data = (NSData *)_CFPropertyListCreateXMLData(kCFAllocatorDefault, (CFPropertyListRef)self, true); 984 BOOL success = [data writeToURL:url atomically:atomically]; 985 [data release]; 986 return success; 987 } 988 989 - (NSArray *)keysSortedByValueUsingSelector:(SEL)comparator 990 { 991 return [[self allKeys] sortedArrayUsingComparator:^NSComparisonResult(id obj1, id obj2) { 992 id val1 = [self objectForKey:obj1]; 993 id val2 = [self objectForKey:obj2]; 994 return (NSComparisonResult)[val1 performSelector:comparator withObject:val2]; 995 }]; 996 } 997 998 - (void)getObjects:(id __unsafe_unretained [])objects andKeys:(id __unsafe_unretained [])keys count:(NSUInteger)count 999 { 1000 if (objects == NULL && keys == NULL) 1001 { 1002 // nothing to do here... move along. 1003 return; 1004 } 1005 1006 NSEnumerator *enumerator = [self keyEnumerator]; 1007 id key = nil; 1008 NSUInteger idx = 0; 1009 while (key = [enumerator nextObject]) 1010 { 1011 if (idx >= count) { 1012 break; 1013 } 1014 1015 if (keys != NULL) 1016 { 1017 keys[idx] = key; 1018 } 1019 1020 if (objects != NULL) 1021 { 1022 objects[idx] = [self objectForKey:key]; 1023 } 1024 1025 idx++; 1026 } 1027 } 1028 1029 - (void)getObjects:(id __unsafe_unretained [])objects andKeys:(id __unsafe_unretained [])keys 1030 { 1031 return [self getObjects: objects andKeys: keys count: [self count]]; 1032 } 1033 1034 - (id)objectForKeyedSubscript:(id)key 1035 { 1036 return [self objectForKey:key]; 1037 } 1038 1039 - (void)enumerateKeysAndObjectsUsingBlock:(void (^)(id key, id obj, BOOL *stop))block 1040 { 1041 [self enumerateKeysAndObjectsWithOptions:0 usingBlock:block]; 1042 } 1043 1044 - (void)enumerateKeysAndObjectsWithOptions:(NSEnumerationOptions)opts usingBlock:(void (^)(id key, id obj, BOOL *stop))block 1045 { 1046 NSArray *keys = nil; 1047 if (opts & NSEnumerationReverse) 1048 { 1049 keys = [[[self allKeys] reverseObjectEnumerator] allObjects]; 1050 } 1051 else 1052 { 1053 keys = [self allKeys]; 1054 } 1055 1056 if (opts & NSEnumerationConcurrent) 1057 { 1058 __block BOOL stop = NO; 1059 dispatch_apply([keys count], dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(size_t iter){ 1060 if (!stop) 1061 { 1062 id key = [keys objectAtIndex:iter]; 1063 id obj = [self objectForKey:key]; 1064 block(key, obj, &stop); 1065 } 1066 }); 1067 } 1068 else 1069 { 1070 id key = NULL; 1071 BOOL stop = NO; 1072 NSUInteger idx = 0; 1073 NSUInteger count = [self count]; 1074 1075 while(idx < count && (key = [keys objectAtIndex:idx]) && !stop) 1076 { 1077 id obj = [self objectForKey:key]; 1078 block(key, obj, &stop); 1079 idx++; 1080 } 1081 } 1082 } 1083 1084 - (NSArray *)keysSortedByValueUsingComparator:(NSComparator)cmptr 1085 { 1086 return [self keysSortedByValueWithOptions:NSSortStable usingComparator:cmptr]; 1087 1088 } 1089 1090 - (NSArray *)keysSortedByValueWithOptions:(NSSortOptions)opts usingComparator:(NSComparator)cmptr 1091 { 1092 return [[self allKeys] sortedArrayWithOptions:opts usingComparator:^NSComparisonResult(id obj1, id obj2) { 1093 id val1 = [self objectForKey:obj1]; 1094 id val2 = [self objectForKey:obj2]; 1095 return cmptr(val1, val2); 1096 }]; 1097 } 1098 1099 - (NSSet *)keysOfEntriesPassingTest:(BOOL (^)(id key, id obj, BOOL *stop))predicate 1100 { 1101 return [self keysOfEntriesWithOptions:0 passingTest:predicate]; 1102 } 1103 1104 - (NSSet *)keysOfEntriesWithOptions:(NSEnumerationOptions)opts passingTest:(BOOL (^)(id key, id obj, BOOL *stop))predicate 1105 { 1106 NSArray *keys = nil; 1107 if (opts & NSEnumerationReverse) 1108 { 1109 keys = [[[self allKeys] reverseObjectEnumerator] allObjects]; 1110 } 1111 else 1112 { 1113 keys = [self allKeys]; 1114 } 1115 1116 __block NSMutableSet *found = [[NSMutableSet alloc] initWithCapacity:[keys count]]; 1117 1118 if (opts & NSEnumerationConcurrent) 1119 { 1120 __block BOOL stop = NO; 1121 dispatch_apply([keys count], dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(size_t iter){ 1122 if (!stop) 1123 { 1124 id key = [keys objectAtIndex:iter]; 1125 id obj = [self objectForKey:key]; 1126 1127 if (predicate(key, obj, &stop)) 1128 { 1129 [found addObject:key]; 1130 } 1131 } 1132 }); 1133 } 1134 else 1135 { 1136 BOOL stop = NO; 1137 NSUInteger idx = 0; 1138 NSUInteger count = [keys count]; 1139 1140 while (!stop && idx < count) 1141 { 1142 id key = [keys objectAtIndex:idx]; 1143 id obj = [self objectForKey:key]; 1144 1145 if (predicate(key, obj, &stop)) 1146 { 1147 [found addObject:key]; 1148 } 1149 1150 idx++; 1151 } 1152 } 1153 1154 NSSet *set = [found copy]; 1155 [found release]; 1156 return [set autorelease]; 1157 } 1158 1159 @end 1160 1161 1162 @implementation NSMutableDictionary (NSExtendedMutableDictionary) 1163 1164 - (void)addEntriesFromDictionary:(NSDictionary *)otherDictionary 1165 { 1166 NSEnumerator *enumerator = [otherDictionary keyEnumerator]; 1167 id key = nil; 1168 1169 while (key = [enumerator nextObject]) 1170 { 1171 [self setObject:[otherDictionary objectForKey:key] forKey:key]; 1172 } 1173 } 1174 1175 - (void)removeAllObjects 1176 { 1177 NSEnumerator *enumerator = [self keyEnumerator]; 1178 id key = nil; 1179 1180 while (key = [enumerator nextObject]) 1181 { 1182 [self removeObjectForKey:key]; 1183 } 1184 } 1185 1186 - (void)removeObjectsForKeys:(NSArray *)keyArray 1187 { 1188 for (id key in keyArray) 1189 { 1190 [self removeObjectForKey:key]; 1191 } 1192 } 1193 1194 - (void)setDictionary:(NSDictionary *)otherDictionary 1195 { 1196 [self removeAllObjects]; 1197 [self addEntriesFromDictionary:otherDictionary]; 1198 } 1199 1200 - (void)setObject:(id)obj forKeyedSubscript:(id <NSCopying>)key 1201 { 1202 [self setObject:obj forKey:key]; 1203 } 1204 1205 @end 1206 1207 @implementation __NSCFDictionary 1208 1209 + (BOOL)automaticallyNotifiesObserversForKey:(NSString *)key 1210 { 1211 return NO; 1212 } 1213 1214 - (void)setObservationInfo:(void *)info 1215 { 1216 _CFDictionarySetKVOBit((CFDictionaryRef)self, (CFIndex)info); 1217 [super setObservationInfo:info]; 1218 } 1219 1220 - (void)removeAllObjects 1221 { 1222 if (_CFDictionaryIsMutable((CFDictionaryRef)self)) 1223 { 1224 CFDictionaryRemoveAllValues((CFMutableDictionaryRef)self); 1225 } 1226 else 1227 { 1228 NSInvalidMutation(); 1229 } 1230 } 1231 1232 - (void)removeObjectForKey:(id)key 1233 { 1234 if (_CFDictionaryIsMutable((CFDictionaryRef)self)) 1235 { 1236 if (key == nil) 1237 { 1238 @throw [NSException exceptionWithName:NSInvalidArgumentException reason:@"Cannot remove a nil key" userInfo:nil]; 1239 return; 1240 } 1241 1242 CFDictionaryRemoveValue((CFMutableDictionaryRef)self, (const void *)key); 1243 } 1244 else 1245 { 1246 NSInvalidMutation(); 1247 } 1248 } 1249 1250 - (void)setObject:(id)obj forKey:(id)key 1251 { 1252 if (_CFDictionaryIsMutable((CFDictionaryRef)self)) 1253 { 1254 if ((obj == nil) || (key == nil)) 1255 { 1256 @throw [NSException exceptionWithName:NSInvalidArgumentException reason:@"Cannot set nil objects nor nil keys" userInfo:nil]; 1257 return; 1258 } 1259 1260 [self willChangeValueForKey:key]; 1261 CFDictionarySetValue((CFMutableDictionaryRef)self, (const void *)key, (const void *)obj); 1262 [self didChangeValueForKey:key]; 1263 } 1264 else 1265 { 1266 NSInvalidMutation(); 1267 } 1268 } 1269 1270 - (NSEnumerator *)keyEnumerator 1271 { 1272 return [[[__NSFastEnumerationEnumerator alloc] initWithObject:self] autorelease]; 1273 } 1274 1275 - (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state objects:(id __unsafe_unretained [])buffer count:(NSUInteger)len 1276 { 1277 return _CFDictionaryFastEnumeration((CFDictionaryRef)self, state, buffer, len); 1278 } 1279 1280 - (id)objectForKey:(id)key 1281 { 1282 if (key == nil) 1283 { 1284 return nil; 1285 } 1286 return (id)CFDictionaryGetValue((CFDictionaryRef)self, (const void *)key); 1287 } 1288 1289 - (NSUInteger)count 1290 { 1291 return CFDictionaryGetCount((CFDictionaryRef)self); 1292 } 1293 1294 - (void)getObjects:(id [])objects andKeys:(id [])keys count:(NSUInteger)count 1295 { 1296 // fastpath: if the provided buffers are large enough, just defer to CFDictionary 1297 if (count >= self.count) { 1298 return [self getObjects: objects andKeys: keys]; 1299 } 1300 1301 // otherwise, do it ourselves 1302 NSUInteger index = 0; 1303 for (id key in self) { 1304 if (index >= count) { 1305 break; 1306 } 1307 1308 keys[index] = key; 1309 objects[index] = [self objectForKey: key]; 1310 ++index; 1311 } 1312 } 1313 1314 - (void)getObjects:(id [])objects andKeys:(id [])keys 1315 { 1316 CFDictionaryGetKeysAndValues((CFDictionaryRef)self, (const void **)keys, (const void **)objects); 1317 } 1318 1319 - (id)mutableCopyWithZone:(NSZone *)zone 1320 { 1321 return (id)CFDictionaryCreateMutableCopy(kCFAllocatorDefault, 0, (CFDictionaryRef)self); 1322 } 1323 1324 - (id)copyWithZone:(NSZone *)zone 1325 { 1326 return (id)CFDictionaryCreateCopy(kCFAllocatorDefault, (CFDictionaryRef)self); 1327 } 1328 1329 - (Class)classForCoder 1330 { 1331 if (_CFDictionaryIsMutable((CFDictionaryRef)self)) 1332 { 1333 return [NSMutableDictionary class]; 1334 } 1335 else 1336 { 1337 return [NSDictionary class]; 1338 } 1339 } 1340 1341 - (NSUInteger)retainCount 1342 { 1343 return CFGetRetainCount((CFTypeRef)self); 1344 } 1345 1346 - (BOOL)_isDeallocating 1347 { 1348 return _CFIsDeallocating((CFTypeRef)self); 1349 } 1350 1351 - (BOOL)_tryRetain 1352 { 1353 return _CFTryRetain((CFTypeRef)self) != NULL; 1354 } 1355 1356 - (oneway void)release 1357 { 1358 CFRelease((CFTypeRef)self); 1359 } 1360 1361 - (id)retain 1362 { 1363 return (id)CFRetain((CFTypeRef)self); 1364 } 1365 1366 - (NSUInteger)hash 1367 { 1368 return CFHash((CFTypeRef)self); 1369 } 1370 1371 - (BOOL)isEqual:(id)obj 1372 { 1373 if (obj == nil) 1374 { 1375 return NO; 1376 } 1377 1378 return CFEqual((CFTypeRef)self, (CFTypeRef)obj); 1379 } 1380 1381 @end 1382 1383 @implementation __NSDictionaryI 1384 1385 + (id)allocWithZone:(NSZone *)zone 1386 { 1387 return (id)[__NSPlaceholderDictionary allocWithZone:zone]; 1388 } 1389 1390 + (BOOL)automaticallyNotifiesObserversForKey:(NSString *)key 1391 { 1392 return NO; 1393 } 1394 1395 + (id)__new:(const id *)objects :(const id *)keys :(NSUInteger)count :(BOOL)immutable :(BOOL)copyKeys 1396 { 1397 NSUInteger sz = count * sizeof(id); 1398 __NSDictionaryI *dict = ___CFAllocateObject2(self, sz * 2); 1399 dict->_used = count; 1400 id *keysAndObjects = (id *)object_getIndexedIvars(dict); 1401 1402 for (NSUInteger idx = 0; idx < count * 2; idx += 2) 1403 { 1404 keysAndObjects[idx] = [keys[idx / 2] copy]; 1405 keysAndObjects[idx + 1] = [objects[idx / 2] retain]; 1406 } 1407 1408 return dict; 1409 } 1410 1411 /* 1412 // add this in when __NSDictionaryM is implemented 1413 - (id)mutableCopyWithZone:(NSZone *)zone 1414 { 1415 1416 } 1417 */ 1418 1419 - (void)dealloc 1420 { 1421 id *keysAndObjects = (id *)object_getIndexedIvars(self); 1422 1423 for (NSUInteger idx = 0; idx < _used * 2; idx += 2) 1424 { 1425 [keysAndObjects[idx] release]; 1426 [keysAndObjects[idx + 1] release]; 1427 } 1428 1429 [super dealloc]; 1430 } 1431 1432 - (id)copyWithZone:(NSZone *)zone 1433 { 1434 return [self retain]; 1435 } 1436 1437 - (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state objects:(id __unsafe_unretained [])buffer count:(NSUInteger)len 1438 { 1439 id *keysAndObjects = NULL; 1440 state->extra[0] = [self count]; 1441 1442 if (state->state >= state->extra[0]) 1443 { 1444 return 0; 1445 } 1446 1447 if (state->extra[1] == 0) 1448 { 1449 state->extra[1] = (unsigned long)object_getIndexedIvars(self); 1450 } 1451 1452 keysAndObjects = (id *)state->extra[1]; 1453 1454 if (state->mutationsPtr == NULL) 1455 { 1456 state->mutationsPtr = &state->extra[0]; 1457 } 1458 1459 id key = keysAndObjects[0]; 1460 NSUInteger idx = 0; 1461 1462 while (key != nil && idx < len && idx < state->extra[0]) 1463 { 1464 buffer[idx] = key; 1465 key = keysAndObjects[idx * 2]; 1466 idx++; 1467 } 1468 1469 state->state += idx; 1470 state->extra[1] += sizeof(id) * idx * 2; 1471 state->itemsPtr = buffer; 1472 return idx; 1473 } 1474 1475 - (void)getObjects:(id __unsafe_unretained [])objects andKeys:(id __unsafe_unretained [])keys count: (NSUInteger)count 1476 { 1477 id *keysAndObjects = (id *)object_getIndexedIvars(self); 1478 1479 for (NSUInteger idx = 0; idx < _used * 2 && idx < count * 2; idx += 2) 1480 { 1481 keys[idx] = keysAndObjects[idx]; 1482 objects[idx] = keysAndObjects[idx + 1]; 1483 } 1484 } 1485 1486 - (void)getObjects:(id __unsafe_unretained [])objects andKeys:(id __unsafe_unretained [])keys 1487 { 1488 return [self getObjects: objects andKeys: keys count: _used]; 1489 } 1490 1491 - (NSEnumerator *)keyEnumerator 1492 { 1493 return [[[__NSFastEnumerationEnumerator alloc] initWithObject:self] autorelease]; 1494 } 1495 1496 - (id)objectForKey:(id)key 1497 { 1498 id *keysAndObjects = (id *)object_getIndexedIvars(self); 1499 // NOTE: This is probably ineffecient and should be refactored somewhat for speed 1500 // thankfully NSDictionaryI should only be used for small dictionaries so the perf 1501 // hit should be relatively small 1502 1503 for (NSUInteger idx = 0; idx < _used * 2; idx += 2) 1504 { 1505 if ([keysAndObjects[idx] hash] == [key hash]) 1506 { 1507 if ([keysAndObjects[idx] isEqual:key]) 1508 { 1509 return keysAndObjects[idx + 1]; 1510 } 1511 } 1512 } 1513 return nil; 1514 } 1515 1516 - (NSUInteger)count 1517 { 1518 return _used; 1519 } 1520 1521 @end