From the docs, 在集合对象中存储可变对象可能会导致问题。如果某些集合包含其变异,则它们可能会变得无效甚至损坏,因为通过变异,这些对象可能会影响它们在集合中的放置方式。首先,如果更改的属性影响对象的散列或isEqual:方法的结果,则作为NSDictionary对象或NSSet对象的散列集合中的键的对象的属性(如果更改)将损坏集合。 (如果集合中对象的哈希方法不依赖于它们的内部状态,则不太可能发生损坏。)其次,如果有序集合中的对象(如排序数组)的属性发生更改,则可能会影响对象的方式与数组中的其他对象进行比较,从而使排序无效。
但是当我记录结果时,对象会完美地添加到集合中。其实这样做不安全吗?
-(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]];
}
}
答案 0 :(得分:2)
代码是安全的。 NSDictionary不关心键值对中值的可变性。它关心的是键的可变性,在你的情况下是NSString(不可变)类型。
同样不要指望如果可变性问题确实存在,它将表现为异常或崩溃。在查询集合时,它可能会被忽略,只是给出了不正确的结果,所以你做的测试没有多大帮助。
答案 1 :(得分:2)
我会告诉你一个例子。
让我们为字典定义我们自己的可变键,注意它可以复制,它定义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
现在让我们有一个简单的测试用例:
让我们定义一些键
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
用于搜索值。一旦我们更改了密钥中的数据,哈希值也会发生变化。因此,字典将无法找到为给定密钥存储的值。