RestKit/RestKit — pull request #2091 by carllindberg (2014-11-14 00:46:15+00:00)
Title: Tiny speedup in RKObjectIsEqualToObject

I may be off base on this one, but it seems to me that RKObjectIsEqualToObject would just be better off calling isEqual: all of the time. I could be missing something important, for example if one of the isEqual: implementations that is not used in my test cases actually is a lot slower. And even in my case, the speedup is very small so it would take millions of calls to notice the difference, which probably won't happen anymore. But still, here goes :-)

Looking at the implementation, the code could be written like this instead:
```
if ([object isKindOfClass:[NSString class]] && [anotherObject isKindOfClass:[NSString class]]) {
return [object isEqualToString:anotherObject];
}
```

Since we know the method to call, we don't really need the cute objc_msgSend trick, although that trick ends up being basically the exact same thing (the compile will generate the exact same thing) so there is no speed penalty, just obviousness of the code.

But... I'm pretty sure that all of the isEqual: methods on such classes just call the corresponding method directly. In other words, they are implemented something like this:
```
- (BOOL)isEqual:(id)other
{
if ([other isKindOfClass:[NSString class]]) {
return [self isEqualToString:other];
}
return NO;
}
```

Or something along those lines. (I have seen that even some isEqualToXXX: implementations also do the isKindOfClass: check, and simply return NO if given an object of the wrong type, in which case they may not even do the check in isEqual:).

If that is the case, then even in the NSString situation, calling -isEqual: directly would result in one -isEqual: objc method call and one -isKindOfClass: call (in the isEqual: implementation presumably) before calling isEqualToString:. In RKObjectIsEqualToObject(), there would be two isKindOfClass: calls before calling isEqualToString: instead, which seems to be about a wash -- not sure there is an advantage. And for objects of other types, that will successively more calls to isKindOfClass: before calling the type-appropriate isEqualToXXX: method, so it seems like it would be slower than just calling isEqual: (which basically lets the receiver automatically handle the isKindOfClass: check on the first object simply by virtue of being that class to begin with).

I did some timings with my real application, which calls this function a few thousand times via relatively heavy usage of RKDynamicMapping (I'll have some questions about that in another pull request). The code as written was pretty consistently about 0.0000028x seconds per call, at least in my use case, which I think is mostly strings and maybe a couple numbers. When I changed to just use isEqual:, it was more like 0.00000223 seconds per call. That is technically about a 20% speedup, but with only a few thousand calls it's not realistically measurable overall.

I added an == check as well. Most of the time, values coming out of JSON will not be the same instances. But in some cases like NSNull, booleans, or certain NSNumbers (via use of the tagged isa pointers in newer architectures) they theoretically could be. I did verify that NSJSONSerialization returned the same instance for an integer value of 1 on successive calls. In my app though, this condition never was true, and so actually slowed things down a tad, to more like 0.00000228 seconds per call. I can take that out if desired since it may hurt more than it helps, but it could help in some situations.

This function can be called a lot if the code is trying to avoid setting unchanged values. If #2080 is merged though, that would avoid most current calls, so the actual performance speedup here may be negligible in the end. It may still be desired just from a code maintenance point of view though. I may have noticed more of a difference from before I made that change locally (which is why it was on my list of things to post); can't remember for sure. It no longer gets called in the test app I'm using for the performance threads, so this has zero difference there.