2011-04-27 54 views
5

我使用NSMutableDictionary来存储对某些类有效的描述符,因为我宁愿不浪费将描述符添加到类的每个实例的内存,因为只有1000个对象的非常小的子集才会有描述。有没有一种干净的方式来使用指针(ids)作为NSMutableDictionary中的键?

不幸的是,鉴于:

MyClass* p = [MyClass thingy]; 
NSMutableDictionary *dict = [NSMutableDictionary dictionary]; 
NSString* description = @"blah"; //does this work? If not I'm just simplifying for this example. 
[dict setObject:description forKey:p]; // BZZZZT a copy of p is made using NSCopying 

MyClass* found = [dict objectForKey:p]; //returns nil, as p becomes a different copy. 

所以这是行不通的。

我可以在一个NSNumber通过像这样破解它:

[dict setObject:description forKey:[NSNumber numberWithInt:(int)p]]; // this is cool 

但是,这不仅难看,而且容易出错,因为它是非标准的。

考虑到这一点,是否有一个干净的方法来做到这一点?

+0

使用NSMapTable代替。请参阅:http:// nshipster。com/nshashtable-and-nsmaptable/ – LearnCocos2D 2015-08-20 13:16:28

回答

7

NSValue确认NSCopying按照NSValue Class Reference。所以你可以使用NSValue的一个实例作为关键。使用+valueWithPointer:将指针值向上包装。

NSString* foo = @"foo"; 
id bar = @"bar"; 

NSMutableDictionary* dict = [[NSMutableDictionary alloc] init]; 
[dict setObject:@"forKey:foo" forKey:foo]; 
[dict setObject:@"forKey:bar" forKey:bar]; 
[dict setObject:@"forKey:[NSValue... foo]" forKey:[NSValue valueWithPointer:foo]]; 
[dict setObject:@"forKey:[NSValue... bar]" forKey:[NSValue valueWithPointer:bar]]; 

NSLog(@"%@", [dict objectForKey:foo]); 
NSLog(@"%@", [dict objectForKey:bar]); 
NSLog(@"%@", [dict objectForKey:[NSValue valueWithPointer:foo]]); 
NSLog(@"%@", [dict objectForKey:[NSValue valueWithPointer:bar]]); 

给人

2013-01-24 04:42:14.051 a.out[67640:707] forKey:foo 
2013-01-24 04:42:14.052 a.out[67640:707] forKey:bar 
2013-01-24 04:42:14.053 a.out[67640:707] forKey:[NSValue... foo] 
2013-01-24 04:42:14.053 a.out[67640:707] forKey:[NSValue... bar] 
+3

在iOS 6中使用ARC我必须这样做才能使用NSValue:[[NSValue valueWithPointer:(__ bridge const void *)(obj)]'。它不会让我使用这个对象而不会投射它。 – 2013-03-16 13:37:11

+3

怎么样:@(((NSInteger)obj)? – Mazyod 2013-11-22 06:36:22

+1

'@((NSInteger)obj)'适合我 – 2014-07-16 11:54:35

2

您的问题是,NSDictionary复制其密钥。要么让你的类实现NSCopying,要么使用一个CFMutableDictionary和一个不复制的密钥回调(它是toll-free bridged和NSMutableDictionary,所以你可以在它创建后使用它)。

1

您可能会发现它更易于使用objc_setAssociatedObject/objc_getAssociatedObject。他们被描述为here

+0

我宁愿坚持在类如何正常运行的约束之内,或根本就不使用它。我不希望我的API使用必须以完全非标准方式使用的NSDictionary才能运行。 – 2011-04-27 12:58:06

+0

您是否回答了错误的答案?我的建议根本不涉及NSDictionary。 – 2011-04-27 14:17:06

+0

糟糕,对不起,我第一次浏览并被误解了。这虽然是一种改进,但并不能帮助我,因为我希望能够根据上下文存储对象,因为多段代码可能会引用同一对象,而且我不希望100%关联,因为我希望其他代码能够看到一个对象是否关联,而不是锁步。 我不确定我是否解释得很好。 – 2011-04-27 21:29:39

0

我个人的解决方案:

覆盖- (BOOL) isEqual:(id)object在你的自定义类。

在此方法中,比较selfobject中的每个属性。如果它们相同,则返回YES

我不确定您是如何实施您的- (id) copyWithZone:(NSZone *)zone方法的,但对于我的测试项目而言,它可以正常工作。

附件:

- (id) copyWithZone:(NSZone *)zone{ 
    testObject *copy = [[testObject allocWithZone:zone] init]; 
    NSString *tempCopyString = [NSString stringWithString:self.string] 
    copy.string = tempCopyString; 
    return copy; 
} 

- (BOOL) isEqual:(id)object{ 
    testObject *newObject = (testObject*)object; 
    if ([newObject.string isEqualToString:object.string]) { 
     return YES; 
    } 
    else { 
     return NO; 
    } 
} 
+0

我的课不是轻量级的,我根本不想创建副本 - 这样做可能会比仅仅为每个可能具有“描述”的类添加指针花费更多。 – 2011-04-27 12:56:38

0

由于这是经常发生的问题,我已经创建了下面的类对象到其他对象没有钥匙的复制关联。身份是在关键对象地址上确定的。 它是NSMutableDictionary的一个子类,因此该类的所有方法都可用。

ObjectMap.h

// 
// ObjectMap.h 
// 
// Created by René Dekker on 07/03/2012. 
// Copyright (c) 2012 Renevision. 
// 

#import <Foundation/Foundation.h> 

@interface ObjectMap : NSMutableDictionary 

+ (ObjectMap *) objectMap; 

- (bool) containsObject:(id)aKey; 

// use the following instead of setObject:forKey: in IOS 6, to avoid warnings about NSCopying 
- (void) setObject:(id)anObject forObjectKey:(id)aKey; 


@end 

ObjectMap.m

// 
// ObjectMap.m 
// 
// Created by René Dekker on 07/03/2012. 
// Copyright (c) 2012 Renevision. 
// 

#import "ObjectMap.h" 
#import <CoreFoundation/CoreFoundation.h> 

@interface ObjectMapEnumerator : NSEnumerator 

@end 

@implementation ObjectMapEnumerator { 
    NSUInteger size; 
    NSUInteger currentIndex; 
    id *keysArray; 
} 

- (NSArray *) allObjects 
{ 
    return [NSArray arrayWithObjects:keysArray count:size]; 
} 

- (id) nextObject 
{ 
    if (currentIndex >= size) { 
     return nil; 
    } 
    return keysArray[currentIndex++]; 
} 

- (id) initWithDict:(CFDictionaryRef)dict 
{ 
    if (!(self = [super init])) { 
     return nil; 
    } 
    size = CFDictionaryGetCount(dict); 
    keysArray = malloc(size * sizeof(id)); 
    currentIndex = 0; 
    CFDictionaryGetKeysAndValues(dict, (const void **)keysArray, NULL); 
    return self; 
} 

- (void) dealloc 
{ 
    free(keysArray); 
    [super dealloc]; 
} 

@end 

@implementation ObjectMap { 
    CFMutableDictionaryRef theDictionary; 
} 

- (void) setObject:(id)anObject forKey:(id)aKey 
{ 
    CFDictionarySetValue(theDictionary, aKey, anObject); 
} 

- (void) setObject:(id)anObject forObjectKey:(id)aKey 
{ 
    CFDictionarySetValue(theDictionary, aKey, anObject); 
} 

- (id) objectForKey:(id)aKey 
{ 
    return CFDictionaryGetValue(theDictionary, aKey); 
} 

- (void) removeObjectForKey:(id)aKey 
{ 
    CFDictionaryRemoveValue(theDictionary, aKey); 
} 

- (void) removeAllObjects 
{ 
    CFDictionaryRemoveAllValues(theDictionary); 
} 

- (bool) containsObject:(id)aKey 
{ 
    return CFDictionaryContainsKey(theDictionary, aKey); 
} 

- (NSUInteger) count 
{ 
    return CFDictionaryGetCount(theDictionary); 
} 

- (NSEnumerator *)keyEnumerator 
{ 
    return [[[ObjectMapEnumerator alloc] initWithDict:theDictionary] autorelease]; 
} 

#pragma - Object Life Cycle 

static void dictionaryRelease(CFAllocatorRef allocator, const void* value) { 
    if (0 != value) { 
     CFRelease(value); 
    } 
} 

static const void *dictionaryRetain(CFAllocatorRef allocator, const void* value) { 
    if (0 != value) { 
     return CFRetain(value); 
    } 
    return 0; 
} 

static CFDictionaryKeyCallBacks callbacks = { 0, &dictionaryRetain, &dictionaryRelease, &CFCopyDescription, NULL, NULL }; 

- (id) init 
{ 
    if (!(self = [super init])) { 
     return nil; 
    } 
    theDictionary = CFDictionaryCreateMutable(NULL, 0, &callbacks, &kCFTypeDictionaryValueCallBacks); 
    return self; 
} 

- (void) dealloc 
{ 
    CFRelease(theDictionary); 
    [super dealloc]; 
} 

+ (ObjectMap *) objectMap 
{ 
    return [[[ObjectMap alloc] init] autorelease]; 
} 

@end 
相关问题