问题
From the docs, Storing mutable objects in collection objects can cause problems. Certain collections can become invalid or even corrupt if objects they contain mutate because, by mutating, these objects can affect the way they are placed in the collection. First, the properties of objects that are keys in hashing collections such as NSDictionary objects or NSSet objects will, if changed, corrupt the collection if the changed properties affect the results of the object’s hash or isEqual: methods. (If the hash method of the objects in the collection does not depend on their internal state, corruption is less likely.) Second, if an object in an ordered collection such as a sorted array has its properties changed, this might affect how the object compares to other objects in the array, thus rendering the ordering invalid.
But when I logs the results, the object is added to the collection perfectly. Actually doing this is not safe??
-(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]];
}
}
回答1:
I will show you an example.
Let's define our own mutable key for a dictionary, note it can be copied, it defines an implementation of hash
and of equality.
@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
Now let's have a simple test case:
Let's define some keys
MyKey *key1 = [[MyKey alloc] initWithData:@"key1"];
MyKey *key2 = [[MyKey alloc] initWithData:@"key2"];
MyKey *keyX = [[MyKey alloc] initWithData:@"XXX"];
And a simple dictionary
NSDictionary *dictionary = @{key1: @"value1", key2: @"value2"};
Let's see what's inside
NSLog(@"%lu", (unsigned long) dictionary.count);
NSLog(@"%@", dictionary);
NSLog(@"%@", [dictionary objectForKey:key1]);
NSLog(@"%@", [dictionary objectForKey:key2]);
NSLog(@"%@", [dictionary objectForKey:keyX]);
gives (expected)
2
{
"<MyKey: 0x10010a8d0>" = value1;
"<MyKey: 0x100108e40>" = value2;
}
value1
value2
(null)
Let's now change the value of a key
[(MyKey *)[[dictionary allKeys] objectAtIndex:0] setKeyData:@"XXX"];
Let's see what's inside again
NSLog(@"%lu", (unsigned long) dictionary.count);
NSLog(@"%@", dictionary);
NSLog(@"%@", [dictionary objectForKey:key1]);
NSLog(@"%@", [dictionary objectForKey:key2]);
NSLog(@"%@", [dictionary objectForKey:keyX]);
Gives
2 //expected
{
"<MyKey: 0x10010a8d0>" = (null); //huh, null value?
"<MyKey: 0x100108e40>" = value2;
}
(null) //huh?
value2
(null) //huh?
What went wrong?
The internal structures of a dictionary are implemented using a hash table. The hash
of they key is used to search a value. Once we change the data in the key, the hash value will also change. As a result, the dictionary won't be able to find the value stored for the given key.
回答2:
The code is safe. NSDictionary does not care about mutability of values in the key-value pairs. What it does care is about mutability of keys which in your case are of NSString (immutable) type.
Also do not expect that if problem with mutability really exists it will manifest itself with exception or crash. It may pass unnoticed just giving incorrect results when querying collection, so the test you did does not help much.
来源:https://stackoverflow.com/questions/20368173/mutable-objects-in-collections