Mutable objects in collections

From the docsSaving modified objects to collection objects can cause problems. Some collections can become invalid or even corrupted if the objects they contain mutate, because by mutating these objects can affect how they are placed in the collection. First, properties of objects that are keys in hashing collections, such as NSDictionary objects or NSSet objects, will, if changed, damage the collection if the changed properties affect the results of the object hash or isEqual: methods. (If the hash method of objects in a collection is independent of their internal state, corruption is less likely.) Second, if an object in an ordered collection, such as a sorted array, has its own properties, it can affect how the object is compared to others. objects in an array,which makes the order invalid.

But when I log the results, the object is added to the collection fine. Is it really unsafe to do this?

-(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]];
  }
}

      

+2


source to share


2 answers


I'll show you an example.

Define our own mutable key for the dictionary, note that it can be copied, it defines implementation hash

and 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 take a look at a simple test case:

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)

      

Now let's change the key value

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

      

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?

      

Something went wrong?

The internal structures of the dictionary are implemented using a hash table. The key is hash

used to find a value. As soon as we change the data in the key, the hash value will also change. As a result, the dictionary cannot find the value stored for the given key.

+2


source


The code is safe. NSDictionary does not care about mutability of values in key-value pairs. To which it takes care of the mutability of the keys , which in your case are of type NSString (immutable).



Also, do not expect that, if a volatility problem does exist, it will manifest itself with an exception or failure. This can go unnoticed by simply giving incorrect results when querying the collection, so the test you did is not very helpful.

+2


source







All Articles