Efficient Objective-C - Part #2

This article is a continuation of part #1, so let’s get to it!


Importing classes

A general rule with importing headers of classes is to do it as little as possible. The more headers imported means longer compile times at best, so remove unused imports whenever possible.

When a class header is imported in another given class header and that given class header is imported to another class, all its own imported classes are imported to that other class too. A lot of the time this is unnecessary.

But worse situations can happen, an example being an infinite inclusion of two classes including each other with #include. At compile time they include each other into an endless loop. Using #import stops the endless loop, but one of the classes incorrectly imports the other class, so the program will not run in most situations. Luckily, there is a way of only declaring a class exists, not importing or including it:

//instead of importing like this
#import DSMPotato
//declare the class like this
@class DSMPotato

Keep in mind that the class may be declared in the header, but imported in the implementation file if its interface information is needed. In essence, use @class whenever possible, only use #import in headers when there is absolutely no other choice (like including frameworks or protocols — which is why protocols must be in their own file).


Global Variables

Try to steer clear of using global variables, that’s rule one. But there will be situations where using them makes sense, like naming NSNotifications and other edge cases. There are a few ways of implementing this in Objective-C, and one I see too much is by using the preprocessor, like this:

#define COMMON_STRING @"common string" 

If this is added to the header of a class, it is then included to every class that imports this class, this is bad. This code doesn’t use the compiler, the preprocessor simply swaps each definition with its value. In our example, COMMON_STRING is swapped to “common string” in all the classes it was imported. Not only can this cause unexpected issues by swapping definitions that weren’t intended to be swapped, it doesn’t imply any type, so it can create type issues at compile time, or even at runtime. A much better choice is to utilize the compiler by using constant variables, like so:

//Naming for local constants
static NSString *const kConstantString = @"common string";
static const CGFloat kConstantFloat = 0.2;

Use this declaration and naming conventions for constants only used locally in an implementation file. Make sure you use static so that the compiler keeps the constant local. As for const, it makes sure the compiler throws an error if the constant value changes.

Using both static and const actually means that the compiler does not create local constants but replaces occurrences, similar to the preprocessor. Keep in mind though that the main difference is that the type information of the constant is declared.

Sometimes we need our constants global though, an example previously mentioned being NSNotification names. In this case, we have to declare the global constants like so:

//Naming for global constants
//In the DSMPotato.h file
extern NSString *const DSMPotatoConstantString;
extern const CGFloat DSMPotatoConstantFloat;
//In the DSMPotato.m file
NSString *const DSMPotatoConstantString = @"common string";
const CGFloat DSMPotatoConstantFloat = 0.2f;

The difference between the two examples is one is an object reference whilst the other is a value reference, leading to different declarations.

Be careful how you name your constant, since it will be visible in the project’s global scope. The naming convention is to prefix the constant with the full name of the class, followed by the constant description. Based on the example, wherever DSMPotato.h will be imported, both constants will be visible too.


Weak References

Since OS X 10.8, the option to have a garbage collector on OS X has been deprecated, and it never existed on iOS. This means we have to take care of memory management. Since ARC was released, this means one of our last main concerns is being careful of retain cycles (along with corner cases, including performSelector and blocks).

A retain cycle is when at least two objects have a strong reference of the other object (or three objects, pointing to the next object, forming a cycle, and so on). This becomes a leak when neither of them are referenced by any other object than each other at runtime. Neither object is of use to the process anymore, yet they are kept alive because of their reference count.

As discussed in our last Efficient Objective-C article, we have two ways of having a weak reference with another object (not raising the reference count of an object even though we have a direct reference to it). These being weak and __unsafe_unretained.

Both properties have similar functions (they do not raise the reference count of the object), but the main difference is what happens when the referenced object’s reference count falls to zero.

The property __unsafe_unretained basically does nothing. Next time when the object is accessed, a dangling pointer is used, which can lead to unexpected behaviour, including a crash.

As for the weak property, once the referenced object is deallocated, the object reference now points to nil instead. This means that if the object is deallocated too quickly, the application not crash, which is favourable from a user point of view, but makes it harder to debug what is happening. The last thing we want is our users experiencing crashes, which is why I recommend using weak instead of __unsafe_unretained whenever possible.

The main situations when we should be using weak references are:

  • When referencing a delegate or a parent object. The delegate design pattern utilizes a non owning relationship.
  • Inside blocks. Whenever referencing self inside of blocks, make sure the reference is weak.
  • Referenced IBOutlet objects should always be weak.
  • Using CF objects (although we use CFRetain() and CFRelease() instead of strong/weak properties).

Grand Central Dispatch vs NSObject’s performSelector

I could write a whole article just about the Grand Central Dispatch. Instead, we will touch base with some less known situations when GCD is our best option.

Before GCD was added to iOS with iOS 4, performSelector was the function used for a whole array of different situations: calling functions that only appeared at runtime, calling functions after a certain amount of time, performing selectors on the main thread, along with other situations. Sadly performSelector (which is a method inherited by NSObject) has many drawbacks, including: input arguments (two maximum) must be objects, limited ability of returning values, possible runtime errors if the selector doesn’t exist and also memory management (ARC cannot know the type, which can cause leaks).

Luckily, GCD can be used instead of performSelector now in many cases, with GCD having many low level optimizations. Let’s start a comparison of both solutions with delayed methods:

//The performSelector solution
[self performSelector:@selector(skinPotato:) withObject:potato afterDelay:5.0];
//The GCD solution
dispatch_time_t dispatchTime = dispatch_time(DISPATCH_TIME_NOW, 5 * NSEC_PER_SEC);
dispatch_after(dispatchTime, dispatch_get_main_queue(), ^{
[self skinPotato:potato];
});

All the issues faced by using performSelector are solved by using GCD instead. The next situation where we should really be using GCD is:

//The performSelector solution
[self performSelectorOnMainThread:@selector(skinPotato:) withObject:potato waitUntilDone:NO];
//The GCD solution
dispatch_async(dispatch_get_main_queue(), ^{
[self skinPotato:potato];
});

No leaks happen with GCD, no runtime errors because of unknown selectors, no issues with input and output values.


Thread safety

Now let’s focus on multithreading. Sometimes when using multithreading, we need to make sure a value is not being accessed at the same time by two threads.

One option is using @synchronized. This is usually a good option, as long as we do not overuse it, since all @synchronized blocks are executed serially. Sadly, @synchronized gets overused a lot.

Another option is directly using the lock object. The issue with this option though, is that deadlocks tend to happen fast.

But there’s another option, you guessed it, GCD. By far the fastest and safest option is using GCD’s barrier blocks. Instead of using a serial queue of operations like @synchronized, GCD uses a concurrent queue, and when asked to, performs barrier blocks. A barrier block is executed serially, other blocks have to wait. Here is a visual example:

Normal and barrier blocks being executed on a concurrent queue.

And here is an example of how to use barrier blocks in Objective-C code:

_potatoQueue = dispatch_queue_create("com.declanmcpartlin.potatoqueue", DISPATCH_QUEUE_CONCURRENT);
- (id)getPotato {
__block DSMPotato* localPotato;
dispatch_sync(_potatoQueue, ^{
localPotato = _potato;
});
return localPotato;
}
- (void)setPotato: (DSMPotato*)potato {
dispatch_barrier_async(_potatoQueue, ^{
_potato = potato;
});
}

As you can see in the example above, regular blocks are being used for simple read operations, but when it comes to operations like write, we make sure no other threads can access the value by setting the block as a barrier block. This makes use of concurrent queues whenever possible, but still keeping the solution thread safe.


So that’s enough for now, thanks for reading, here’s a potato.