From 45933665cf05964142e162006b4dca402555a71b Mon Sep 17 00:00:00 2001 From: Jesse Squires Date: Sun, 27 Nov 2016 17:33:23 -0800 Subject: [PATCH] Update IGListDiffable and Equality.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Summary: Some minor tweaks and improvements to this guide. 😄 Closes https://github.com/Instagram/IGListKit/pull/265 Differential Revision: D4235752 Pulled By: jessesquires fbshipit-source-id: 03adec7750ac8ded23542db97b9506ff7dc00adf --- Guides/IGListDiffable and Equality.md | 65 +++++++++++++++------------ 1 file changed, 36 insertions(+), 29 deletions(-) diff --git a/Guides/IGListDiffable and Equality.md b/Guides/IGListDiffable and Equality.md index 8cfd087b..90da7283 100644 --- a/Guides/IGListDiffable and Equality.md +++ b/Guides/IGListDiffable and Equality.md @@ -1,10 +1,14 @@ # IGListDiffable and Equality -This guide details how to write a good `isEqual:` method. `IGListKit` requires that models implement the method `isEqualToDiffableObject:` which should perform the same type of check, but without impacting behavior in Objective-C containers like `NSDictionary` and `NSSet`. +This guide details how to write good `isEqual:` methods. -## IGListDiffable bare minimum +## Background -The quickest way to get started with diffable models is use the _object itself_ as the identifier, and use the super `-[NSObject isEqual:]` implementation for equality: +`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: ```objc - (id)diffIdentifier { @@ -18,27 +22,22 @@ The quickest way to get started with diffable models is use the _object itself_ ## Writing better Equality methods -Even though `IGListKit` uses the method `isEqualToDiffableObject:`, the concepts of writing a good equality check apply in general. +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. -Here are the basics to writing good `-isEqual:` and `-hash` functions. Note this is all ObjC but translates well to Swift. - -- If you override `-isEqual:` you **must** override `-hash` - - Check out this [article by Mike Ash](https://www.mikeash.com/pyblog/friday-qa-2010-06-18-implementing-equality-and-hashing.html) for details -- Always compare the pointer first - - Saves a lot of wasteful `objc_msgSend(...)` and value comparisons if checking the same instance -- When comparing object values, always check for `nil` before `-isEqual:` - - e.g. `[nil isEqual:nil]` returns `NO` - - Instead do `left == right || [left isEqual:right]` -- Always compare the **cheapest values first** - - Doing `[self.array isEqual:other.array] && self.intVal == other.array` is hella wasteful if the `intVal`s are different. Use lazy eval! +- If you override `-isEqual:` you **must** override `-hash`. Check out this [article by Mike Ash](https://www.mikeash.com/pyblog/friday-qa-2010-06-18-implementing-equality-and-hashing.html) 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 returns `NO`. Instead, do `left == 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 the `intVal` values are different. Use lazy evaluation! As an example, if I had a `User` model with the following interface: ```objc @interface User : NSObject -@property NSInteger pk; + +@property NSInteger identifier; @property NSString *name; @property NSArray *posts; + @end ``` @@ -48,39 +47,47 @@ You would implement its equality methods like so: @implementation User - (NSUInteger)hash { - return self.pk; + return self.identifier; } - (BOOL)isEqual:(id)object { - if (self == object) return YES; - if (![object isKindOfClass:[User class]]) return NO; + if (self == object) { + return YES; + } + + if (![object isKindOfClass:[User class]]) { + return NO; + } User *right = object; - return self.pk == right.pk - && (self.name == right.name || [self.name isEqual:right.name]) - && (self.posts == right.posts || [self.posts isEqualToArray:right.posts]); + 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 +## Using both `IGListDiffable` and `-isEqual:` -Making your objects work universally with Objective-C containers and `IGListKit` is really easy once you've implemented `isEqual:` and `hash`. +Making your objects work universally with Objective-C containers and `IGListKit` is easy once you've implemented `isEqual:` and `-hash`. ```objc -@interface User (IGListDiffable) +@interface User + +// properties... + @end -@implementation User (IGListDiffable) +@implementation User - (id)diffIdentifier { - return @(self.pk); + return @(self.identifier); } - (BOOL)isEqualToDiffableObject:(id)object { - return [self isEqual:object]; + return [self isEqual:object]; } @end -``` \ No newline at end of file +```