Ben W
Ben W

Reputation: 1313

containsObject: returning NO when isEqual and hash match

I have an NSMutableOrderedSet that I'm adding and removing MTLModels from. The hash and isEqual methods will both return true for two objects, but containsObject will return false.

Sometimes this code works and sometimes it doesn't.

models count: 1
isEqual: 1
hashes equal: 1
containsObject: 0

How is it possible the below code could print out the above?

@property (nonatomic, strong) NSMutableOrderedSet *models;

- (void)remove:(MTLModel *)model {
  NSLog(@"models count: %d", self.models.count);
  MTLModel *modelInSet = (MTLModel *)self.models.firstObject;
  NSLog(@"isEqual: %d", [modelInSet isEqual:model]);
  NSLog(@"hashes equal: %d", modelInSet.hash == model.hash);
  NSLog(@"containsObject: %d", [self.models containsObject:model]);
}

Update:

As a follow-up, this returns YES when the NSMutableOrderedSet returns NO:

[[self.models array] containsObject:model]

Update 2:

If I check if modelInSet is contained in self.models, that also returns NO, even though it's the object returned by firstObject.

Upvotes: 5

Views: 936

Answers (1)

fumoboy007
fumoboy007

Reputation: 5543

You are probably mutating the object after adding it to the set. This is not allowed. The set does not recalculate hashes, so if you mutate the object, it will have a different hash from the one stored by the set. This is why creating a new collection (array in your case) works, i.e. because it recalculates all the hashes.

EDIT: To be clear, you can mutate an object after adding it to the set, but its hash value must not change.

Upvotes: 5

Related Questions