views:

83

answers:

1

I have a set of classes to represent some objects loaded from a database. There are a couple variations of these objects, so I have a common base class and two subclasses to represent the differences. One of the key fields they have in common is an id field.

Unfortunately, the id of an object is not unique across all variations, but within a single variation. What I mean is, a single object of type A could have an id between, say, 0 and 1,000,000. An object of type B could have an id between, 25,000 and 1,025,000. This means there's some overlap of id numbers. The objects are just variations of the same kind of thing, though, so I want to think of them as such in my code. (They were assigned ids from different sets for legacy reasons.)

So I have classes like this:

@class BaseClass
@class TypeAClass : BaseClass
@class TypeBClass : BaseClass

BaseClass has a method (NSNumber *)objectId. However instances of TypeA and TypeB could have overlapping ids as discussed above, so when it comes to equality and putting these into sets, I cannot just use the id alone to check it.

The unique key of these instances is, essentially, (class + objectId). So I figured that I could do this by making the following hash function on the BaseClass:

-(NSUInteger)hash
{
    return (NSUInteger)[self class] ^ [self.objectId hash];
}

I also implemented isEqual like so:

- (BOOL)isEqual:(id)object
{
    return (self == object) || ([object class] == [self class] && [self.objectId isEqual:[object objectId]]);
}

This seems to be working, but I guess I'm just asking here to make sure I'm not overlooking something - especially with the generation of the hash by using the class pointer in that way. Is this safe or is there a better way to do this?

+2  A: 

This is probably safe, but not necessarily. Depending on strict class identity can bite you if you actually wind up with a subclass somehow (e.g. if KVO causes your class to be switched for another*). It would probably be a little bit safer if use some sort of explicit class ID.

Also keep in mind that unequal objects do not need to have different hashes. The only requirement is that objects that are equal must have the same hash. So it's OK if objects in the two classes have the same hash as long as this doesn't wind up slowing your hash tables too much.

(*I honestly don't remember off the top of my head if the secret KVO subclass masquerades as the parent class, in which case you'd still be safe here, but I generally try to avoid depending on class identity.)

Chuck
Ah - so hash only needs to return the same thing if isEqual is YES. That makes sense and is easier than I was thinking about it. So I can just return the hash of the objectId alone. (There is id overlap, but in practice it's not common - just something that I wanted to make sure wouldn't cause problems.) I haven't found a certain answer about how KVO affects all this yet, but I did change isEqual: to use isKindOf: rather than comparing the classes directly with ==. Thanks!
Sean