Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Duplicated custom object in NSSet

I have some problems about the NSMutableSet in Objective-C. I learnt that the NSSet will compare the two objects' hash code to decide whether they are identical or not. The problems is, I implemented a class that is subclass of NSObject myself. There is a property NSString *name in that class. What I want to do is when instances of this custom class has the same variable value of "name" , they should be identical, and such identical class should not be duplicated when adding to an NSMutableSet.

So I override the - (NSUInteger)hash function, and the debug shows it returns the same hash for my two instances obj1, obj2 (obj1.name == obj2.name). But when I added obj1, obj2 to an NSMutableSet, the NSMutableSet still contained both obj1, obj2 in it.

I tried two NSString which has the same value, then added them to NSMutableSet, the set will only be one NSString there.

What could be the solution? Thank you for any help!

The custom Class: Object.h:

#import <Foundation/Foundation.h>

@interface Object : NSObject

@property (retain) NSString *name;

@end

Object.m

@implementation Object
@synthesize name;

-(BOOL)isEqualTo:(id)obj {
    return [self.name isEqualToString:[(Object *)obj name]] ? true : false;
}

- (NSUInteger)hash {
    return [[self name] hash];

}
@end

and main:

#import <Foundation/Foundation.h>
#import "Object.h"

int main(int argc, const char * argv[])
{
    @autoreleasepool {
        Object *obj1 = [[Object alloc]init];
        Object *obj2 = [[Object alloc]init];
        obj1.name = @"test";
        obj2.name = @"test";
        NSMutableSet *set = [[NSMutableSet alloc] initWithObjects:obj1, obj2, nil];
        NSLog(@"%d", [obj1 isEqualTo:obj2]);
        NSLog(@"%ld", [set count]);
    }
    return 0;
}
like image 789
john Avatar asked Oct 25 '13 02:10

john


1 Answers

Instead of implementing isEqualTo: you have to implement isEqual:

- (BOOL)isEqual:(id)object {
    return [object isKindOfClass:[MyObject class]] &&
           [self.name isEqual:[(MyObject *)object name]];
}

This will (probably falsely) return NO if both self.name and object.name are nil. If you want to return YES if both properties are nil you should use

- (BOOL)isEqual:(id)object {
    if ([object isKindOfClass:[MyObject class]]) {
        return (!self.name && ![(MyObject *)object name]) ||
        [self.name isEqual:[(MyObject *)object name]];
    }
    return NO;
}
like image 198
Sebastian Avatar answered Oct 28 '22 16:10

Sebastian