I thought it might be interesting to share the differences between the isEqual methods used by NSDictionary and my new class to shed some more light on the situation.
Here’s the way I set up Skill Plan items prior to writing the custom class:
[[NSDictionary dictionaryWithObjectsAndKeys: [EVESkill skillWithID:@2345], @“skill”, [NSNumber numberWithInteger:1], @“level”, nil];
Here’s the way that I set them up now:
[EVESkillPlanItem skillPlanItemWithSkill: [EVESkill skillWithID:@2345] atLevel:1];
Now, I can’t speak specifically to the code that Apple uses to write NSDictionary’s isEqual method, but here are the things that are called when I profiled with that first method:
-[NSDictionary isEqual:]
-[NSDictionary isEqualToDictionary:]
-[NSDictionary countForKey:]
-[NSDictionary objectForKey:]
-[__NSDictionaryl objectForKey:]
-[EVESkill isEqual:]
-[__NSCFNumber isEqual:]
-[__NSCFNumber isEqualToNumber:]
…
And on and on and on! All of this to find whether the dictionary I’m looking for is contained in the set. It would be much easier if there were some quicker way of doing this, and there is.
The reason that the object method is so much faster is that it sets up not only a custom isEqual method, but a hash function that speeds up the lookups. If you haven’t looked into hashing and buckets, Apple has a good video from WWDC 13 called “Designing Code for Performance” which discusses hash-based organization, about halfway through the video (note that you will need a developer account to watch the video):
Long story short, things are arranged in buckets based on the value of the hash function. If there are multiple objects with the same hash, then the hash is not sufficient to check for equality in the set or array. What does NSDictionary return for hash? I wrote a small piece of code that outputs the hash function for a dictionary of random elements with random keys. And it turns out that in this case the hash is equal to the number of objects in the table. No wonder it is so spectacularly slow for my case. Every dictionary must be compared.
So here’s a better hash function for my class:
-(NSUInteger)hash {
return _skill.hash ^ _level;
}
Easy. And it produces a hash function that is much closer to uniqueness than the dictionary method which was exactly the opposite of unique. I don’t know if it is truly unique, but it certainly collides less than “every time.” For clarity, _skill.hash basically just returns the internal Skill ID number from the EVE Database, which is guaranteed to be unique. The bitwise or with the level number makes it such that the same skill of a different level does not produce the same hash.
So there you have it. When we need to know if the EVESkillPlanItem is in the Skill Plan NSOrderedSet, we have an O(1) lookup instead of an O(n). Pretty nifty, eh?






