Question

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]];
  }
}
Was it helpful?

Solution

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.

OTHER TIPS

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.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top