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
nil
before-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.array
is extremely wasteful if theintVal
values 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