Thursday, June 5, 2014

Tips for efficient objective c code - I

Before thinking about code optimizations it is better to refresh about how an objective C code would run.
The preprocessor
Before the C compiler the preprocessor takes over. This is invoked automatically by the compiler before the compilation process. Here the preprocesssor modifies your source file. compiler uses this modified file for the compilation process. Preprocessor converts the prepocessor commands in to the meaningful source code. For example consider define command. This is mainly used to define constants. For instance,
If you have
    int no = NUMBER;
prepocessor converts this as follows.
    int no = 100;

#include 
Here the preprocessor pasts the content of header file to the at the location of the #include directive.
Compiler
Next the compiler takes over and converts the source file into an object file. The file ends in .o. This file is not directly executable. It lacks sources code for library functions that were included by #include directive.
Linker
Just like the preprocessor linker is a separate program. What linker does is link together several object files into a single binary executable. Here these several object files means following files
  1. object file created in the compilation process
  2. object files that are pre compiled and collected as library files
Also when we talk about optimization, it is better to know more about objective c. Objective C unlike Java or C++ uses messaging. NOT FUNCTION CALLING. When messaging is used the runtime decides which code to execute. But in function calling it's compiler. If you are familiar with C++, you should know about the virtual table, which is used for run time look ups. It was used to support the polymorphism feature. But in objective c, since we are using messaging, the look up is always at the runtime. In objective c, bulk of the work is done by the runtime. The runtime contains all the data structures for OOP features, memory management functions etc. because of this, unlike other languages, when the runtime is updates and optimized, you app will get more faster.
minimizing header imports
if you want to import something, except it is super class header or a protocol you can use forward declaration. In other words if your header file only needs that the class to be imported exist, always use a forward declaration. And in the .m file use the header file import. As I pointed out if you are using protocols, you need to import the header file anyway. So make sure that the protocol contains only the method needed by the the particular class. If it contains methods that are not currently used by the current class, it is better to split the protocol in to few files.
Read instance variables directly but write them using the property
When you directly access the the variable you can bypass the getter code generated by the compiler. So it is much faster. directly setting the variable without using the property is also the same. But if you are not careful it may lead to bugs that are very hard to debug. Since when you directly access, the code including the memory management code generated by the compiler will be bypassed. For example if your property is declared as copy, if you use the direct access to modify the variable, the variable will not be copied. The new value will be retained and the old value will be released. In addition if you are using KVO, KVO will not be fired in direct access. So for the modifying it is safe not to use direct accessing.
Overiding -(NSUIInteger)hash methods correctly.
This method and -(BOOL)isEqual method plays a very important part when determining equality of two objects. If isEqual is returning true for two objects, it should always return the same value in the hash method. But objects that return the same hash value, may not return true in isEqual. This should be understandable if you are familiar with hashing. If there is a collision different objects goes to the same hash bucket, then we have to use something like double hashing or linear probing to correctly index the item. According to Apple WWDC 2013 Apple uses linear probing in their hashing algorithms. Assume you have a hash method as follows
-(NSUIInteger)hash {
    returns 1345435;
}
This is an absolutely correct implementation and hash code computation is super fast. But there is a huge drawback. If these objects are inserted in to a collection which employes hashing, it will lead to big performance issues. Because hash is used as an index within the hash table that the collection uses. For example assume you are usng a set. And you are using above mentioned hash function. Here set uses the hash function to put each objects in the internal representation. Since the hash code is a constant, all objects will end up in the same hash bucket. So you will not gain any performance gain by hashing. So hard coding the hash is not the best approach. consider the following implementation.
-(NSUInteger)hash {
     NSString *stringHash = [NSString stringWithFormat:@"%@:%@:%i",_firstName, _lastName, _age];
     return [stringHash hash];
 }
Here the implementation is correct since when isEqual return true, hash function will return the same value. But the hash function is slow, since a string is created.
-(NSUInteger)hash {
    NSUInteger nameHash = [_firstName hash];
    NSUInteger lastNameHash = [_lastName hash];
    NSUInteger ageHash = _age;
    
    return nameHash^lastNameHash^ageHash;
 }
This function is much faster than the earlier approach. And have a good range of hashes as well. So this is a better compromise between the speed of the hash algorithm and the range of hashes.