IGListDiffable and Equality
This guide details how to write good isEqual: methods.
Background
IGListKit requires that models implement the method isEqualToDiffableObject: which should perform the same type of check as isEqual:, but without impacting performance characteristics like in Objective-C containers such as NSDictionary and NSSet.
IGListDiffable bare minimum
The quickest way to get started with diffable models is use the object itself as the identifier, and use the superclass’s -[NSObject isEqual:] implementation for equality:
- (id<NSObject>)diffIdentifier {
return self;
}
- (BOOL)isEqualToDiffableObject:(id<IGListDiffable>)object {
return [self isEqual:object];
}
Writing better Equality methods
Even though IGListKit uses the method isEqualToDiffableObject:, the concepts of writing a good equality check apply in general. Here are the basics to writing good -isEqual: and -hash functions. Note this is all Objective-C but applies to Swift also.
- If you override
-isEqual:you must override-hash. Check out this article by Mike Ash for details. - Always compare the pointer first. This saves a lot of wasteful
objc_msgSend(...)calls and value comparisons if checking the same instance. - When comparing object values, always check for
nilbefore-isEqual:. For example,[nil isEqual:nil]unintuitively returnsNO. Instead, doleft == right || [left isEqual:right]. - Always compare the cheapest values first. For example, doing
[self.array isEqual:other.array] && self.intVal == other.arrayis extremely wasteful if theintValvalues are different. Use lazy evaluation!
As an example, if I had a User model with the following interface:
@interface User : NSObject
@property NSInteger identifier;
@property NSString *name;
@property NSArray *posts;
@end
You would implement its equality methods like so:
@implementation User
- (NSUInteger)hash {
return self.identifier;
}
- (BOOL)isEqual:(id)object {
if (self == object) {
return YES;
}
if (![object isKindOfClass:[User class]]) {
return NO;
}
User *right = object;
return self.identifier == right.identifier
&& (self.name == right.name || [self.name isEqual:right.name])
&& (self.posts == right.posts || [self.posts isEqualToArray:right.posts]);
}
@end
Using both IGListDiffable and -isEqual:
Making your objects work universally with Objective-C containers and IGListKit is easy once you’ve implemented isEqual: and -hash.
@interface User <IGListDiffable>
// properties...
@end
@implementation User
- (id<NSObject>)diffIdentifier {
return @(self.identifier);
}
- (BOOL)isEqualToDiffableObject:(id<IGListDiffable>)object {
return [self isEqual:object];
}
@end
View on GitHub
IGListDiffable and Equality Reference