集合中的可变对象

从文档中 ,将可变对象存储在集合对象中可能会导致问题。 某些集合可能会变得无效甚至损坏,如果它们包含mutate的对象,因为通过变异,这些对象可能会影响它们被放置在集合中的方式。 首先,如果更改的属性影响对象的散列或isEqual:方法的结果,则散列集合(如NSDictionary对象或NSSet对象)中的键的对象的属性(如果更改)将会损坏集合。 (如果集合中的对象的散列方法不依赖于它们的内部状态,那么腐败就不太可能)。其次,如果有序集合(如sorting数组)中的对象的属性发生更改,则可能会影响对象与数组中的其他对象进行比较,从而导致sorting无效。

但是当我logging结果时,对象被完美地添加到集合中。 其实这样做是不安全的?

-(void)mutableObjectsinCollections{ NSMutableArray *arr1 = [[NSMutableArray alloc]init]; NSMutableArray *arr2 = [[NSMutableArray alloc]init]; NSMutableArray *arr3 = [[NSMutableArray alloc]init]; NSMutableArray *arr4 = [[NSMutableArray alloc]init]; [self addObjectsToArrayBefore:arr1]; [self addObjectsToArrayBefore:arr2]; [self addObjectsToArrayBefore:arr3]; [self addObjectsToArrayBefore:arr4]; NSDictionary *dict = @{@"arr1": arr1,@"arr2": arr2,@"arr3": arr3,@"arr4": arr4,@"arr5": arr5}; NSLog(@"%@",dict); [self addObjectsToArrayAfter:arr1]; [self addObjectsToArrayAfter:arr2]; [self addObjectsToArrayAfter:arr3]; [self addObjectsToArrayAfter:arr4]; NSLog(@"%@",dict); } -(void)addObjectsToArrayBefore:(NSMutableArray *)arr{ for (int i = 1; i<10; i++) { [arr addObject:[NSNumber numberWithInteger:i]]; } } -(void)addObjectsToArrayAfter:(NSMutableArray *)arr{ for (int i = 10; i<20; i++) { [arr addObject:[NSNumber numberWithInteger:i]]; } } 

我会告诉你一个例子。

让我们为字典定义我们自己的可变关键字,注意它可以被复制,它定义了hash和相等的实现。

 @interface MyKey : NSObject <NSCopying> @property (nonatomic, copy, readwrite) NSString *keyData; @end @implementation MyKey - (id)initWithData:(NSString *)data { self = [super init]; self.keyData = data; return self; } - (id)copyWithZone:(NSZone *)zone { MyKey *key = (MyKey *) [[[self class] alloc] init]; key.keyData = self.keyData; return key; } - (NSUInteger)hash { return self.keyData.length; } - (BOOL)isEqual:(id)object { if (![object isMemberOfClass:[self class]]) { return NO; } MyKey *key = object; return [self.keyData isEqualToString:key.keyData]; } @end 

现在让我们来看一个简单的testing用例:

我们来定义一些键

 MyKey *key1 = [[MyKey alloc] initWithData:@"key1"]; MyKey *key2 = [[MyKey alloc] initWithData:@"key2"]; MyKey *keyX = [[MyKey alloc] initWithData:@"XXX"]; 

还有一个简单的字典

 NSDictionary *dictionary = @{key1: @"value1", key2: @"value2"}; 

让我们看看里面是什么

 NSLog(@"%lu", (unsigned long) dictionary.count); NSLog(@"%@", dictionary); NSLog(@"%@", [dictionary objectForKey:key1]); NSLog(@"%@", [dictionary objectForKey:key2]); NSLog(@"%@", [dictionary objectForKey:keyX]); 

给(预期的)

 2 { "<MyKey: 0x10010a8d0>" = value1; "<MyKey: 0x100108e40>" = value2; } value1 value2 (null) 

现在让我们改变一个键的值

 [(MyKey *)[[dictionary allKeys] objectAtIndex:0] setKeyData:@"XXX"]; 

让我们再看看里面是什么

 NSLog(@"%lu", (unsigned long) dictionary.count); NSLog(@"%@", dictionary); NSLog(@"%@", [dictionary objectForKey:key1]); NSLog(@"%@", [dictionary objectForKey:key2]); NSLog(@"%@", [dictionary objectForKey:keyX]); 

 2 //expected { "<MyKey: 0x10010a8d0>" = (null); //huh, null value? "<MyKey: 0x100108e40>" = value2; } (null) //huh? value2 (null) //huh? 

什么地方出了错?

字典的内部结构使用哈希表来实现。 他们的密钥hash用来search一个值。 一旦我们改变了密钥中的数据,哈希值也会改变。 结果,字典将无法find为给定键存储的值。

代码是安全的。 NSDictionary不关心键值对中值的可变性。 它所关心的是关于在你的情况下是NSString(不可变)types的键的可变性。

另外不要指望,如果确实存在可变性问题,它将以exception或崩溃的formsperformance出来。 查询收集时可能会忽略不计,只是给出不正确的结果,所以你做的testing没有多大帮助。