Wednesday, April 20, 2011

Using view controllers wisely

View controllers are fundamental objects in the iOS world. As a programmer, you create view controllers all the time, but have you ever thought about how to use them wisely? View controller themselves can be quite fat objects (especially their associated view), and using them sloppily can lead to a waste of precious resources.

In this article, I discuss issues related to how view controllers are displayed and managed, and solutions to (hopefully) make your life easier as an iOS programmer.

View controller containers

View controller containers are objects whose responsibility is to manage some set of view controllers, displaying or hiding them as required. Often, containers are view controllers themselves (UITabBarController, UINavigationController or UISplitViewController, for example), but this is is by no means required (UIPopoverController, for example, directly inherits from NSObject).

Writing custom view controllers is not an easy task and requires an entire dedicated post. I here only briefly mention two important properties that well-written view controller containers must exhibit:

  • Containers must correctly forward lifecycle and rotation events to the view controllers they manage. There is namely no magic free mechanism propagating those events from the container to its contents, the container implementation is solely responsible of making it right. A badly implemented container must be avoided at any cost since it is poisonous. It namely requires the view controllers it manages to implement its non-standard lifecycle, making them very difficult to reuse elsewhere
  • Containers must retain the view controllers they manage. This is consistent with existing built-in UIKit containers, and it would be really bad if this was not the case. This would put more burden on clients which, after all, cannot in general know when the container is done with its contents

  • UIKit view controller containers of course satisfy the above requirements. Be especially careful when using containers from other sources, though. In particular check that the above two properties are satisfied. If this is not the case, I strongly advise you not to use them.

    Displaying view controllers

    No matter what you intend to do, any application you write most probably begins with a root view controller. This is the view controller whose view is directly added as first subview of the main window. This view controller is not retained anywhere, you must therefore do it yourself (e.g. in your application delegate). Apple engineers took care of correctly sending lifecycle and rotation events to the root view controller of an application (otherwise we would obviously have had a problem). This view controller is therefore the starting point of your view controller hierarchy.

    When displaying a non-root view controller, several options are available:

  • If you have a view controller container at hand, you can use it to display your new view controller (e.g. as a new navigation level in a UINavigationController)
  • You can cover an existing view controller modally with the new one using +[UIViewController presentModalViewController:animated:]
  • You can display the new view controller by adding its view as subview of an existing view controller's view
  • You can display the new view controller by adding its view as subview of the main window (above the already added root view controller's view)

  • The third option is extremely dangerous and must be avoided, except if you are a container implementer. You namely become entirely responsible of forwarding lifecycle and rotation events correctly, which is very difficult to get right. You definitely do not want to face such issues if you are not implementing library or framework components. Of course, you can still get your application to work this way, but the code is likely to get ugly or to be a nightmare to understand. Usually, you namely end up putting all stuff into the viewDidLoad method since the other lifecycle methods cannot be trusted. That's bad. Don't do it. Period.

    The fourth option is tempting when you need to display a view controller quasi-modally (i.e. with the covered view controller still visible). It is dangerous as well, though: Rotation events are sent correctly to the first subview of the window, but not to subsequent ones. You therefore need to find some way to get these events to be sent to view controllers you display quasi-modally, otherwise they will not rotate properly. Moreover, you need to retain the view controller somewhere (as for the root view controller, most probably in your application delegate).

    You should therefore always use well-written view controller containers or +[UIViewController presentModalViewController:animated:] to display view controllers. This guarantees that you can trust view controller lifecycle and rotation events to occur as expected. The only exception to this rule is the root view controller of your application, which you directly add as first subview of your application window.

    You must retain the root view controller of your application, but what about the other view controllers in your application? Should you retain them as well? Let us discuss this issue now.

    Managing view controllers

    For convenience, we often need to keep references to view controllers. Since the first view controller of an application has to be retained, it can be pretty tempting to think this rule can be applied everywhere as well. This is especially appealing for novice iOS programmers since it makes them confident they will never access dead view controllers.

    But this is wrong. Keeping strong references to view controllers should be the exception, not the rule. Retaining view controllers where not necessary is most of the time not only useless, but also a serious waste of resources. While this can make sense in very special cases I will discuss below, if you need to keep a reference to a view controller (except the root one), a weak one suffices. As said in the previous section, you should namely always present view controllers modally or using well-behaved containers, and in all such cases the view controller you display will be retained for you. Over-retaining a view controller (and its associated view) is the easiest way to keep it in memory even if you are not using it anymore.

    There is only one reason you may sometimes need to keep several additional strong references to view controllers: Caching. A container probably releases a view controller it manages when it is done with it, but sometimes you might be pretty confident that the view controller will be used again very soon. In such cases, and especially if the associated view is costly to create, it might make sense to keep the view controller alive even after the container is done with it.

    Once you introduce such a cache mechanism, though, you become responsible of releasing these cached objects when memory gets critically low. If the view controllers strong references are kept by another "owner" view controller, you should take the following measures:

  • In the viewDidUnload method of the owner view controller, set the view property of every cached view controller to nil, and forward the viewDidUnload event to them afterwards
  • In the didReceiveMemoryWarning method of the owner view controller, set the view property of every currently invisible view controller to nil, and forward the viewDidUnload event to them afterwards. You can even release the view controllers themselves if you want, but be sure to implement some lazy creation mechanism so that the view controllers can be created again when needed

  • If the view controllers are not cached by a view controller, listen to memory warning notifications (UIApplicationDidReceiveMemoryWarningNotification) and, in your notification callback, proceed as for didReceiveMemoryWarning above.

    Conclusion

    Always use high-quality view controller containers or +[UIViewController presentModalViewController:animated:] to display view controllers in your application. If you need to keep a reference to a view controller somewhere, use a weak reference, except if you really want it to stay alive longer for caching purposes. In such cases, be sure to correctly respond to low-memory conditions.

    There is one exception to these rules: The first view controller of your application, which you directly add as first window subview, and which you must retain.

    In an upcoming article, I will thoroughly discuss the problems we face when implementing a view custom controller container. Stay tuned.

    Remark

    Starting with iOS 4, UIWindow has a new rootViewController property you can use to set... the root view controller of your application. Can you guess which semantics this property has been given without having a look at the UIWindow header files? (thanks 0xced for pointing this out)

    Wednesday, April 6, 2011

    Avoid - [NSNotification removeObserver:]

    Recently, I found myself implementing a UIViewController which required me to shift the associated view up and down as the keyboard appeared or disappeared. Since it was only necessary to catch these events when the view was actually visible, I decided to listen to keyboard notifications when the view was visible only:
    // YoYoViewController.m
    
    @implementation YoYoViewController
    
    #pragma mark View lifecycle events
    
    - (void)viewDidAppear:(BOOL)animated
    {
        [super viewDidAppear:animated];
    
        [[NSNotificationCenter defaultCenter] addObserver:self 
                                                 selector:@selector(keyboardWillShow:) 
                                                     name:UIKeyboardWillShowNotification 
                                                   object:nil];
        [[NSNotificationCenter defaultCenter] addObserver:self 
                                                 selector:@selector(keyboardWillHide:) 
                                                     name:UIKeyboardWillHideNotification 
                                                   object:nil];
    }
    
    - (void)viewDidDisappear:(BOOL)animated
    {
        [super viewDidDisappear:animated];
    
        [[NSNotificationCenter defaultCenter] removeObserver:self];
    }
    
    - (void)viewDidUnload
    {
        [super viewDidUnload];
    
        // Release associated views here
    }
    
    #pragma mark Keboard notification events
    
    - (void)keyboardWillShow:(NSNotification *)notification
    {
        // Shift the view up
    }
    
    - (void)keyboardWillHide:(NSNotification *)notification
    {
        // Shift the view down
    }
    
    // ...
    
    @end
    
    I can be quite lazy sometimes, and since I was not registering for any other notifications, I decided to save myself some typing by simply using - [NSNotification removeObserver:] to stop listening to keyboard notification events when the view disappeared.

    Soon afterwards, I was testing my application, and I found it surprisingly robust when simulating memory warnings in the iOS simulator. I even congratulated my fellow colleagues about the nice work we had done taking all necessary measures so that the application behaved nicely when running out of memory. Well, at least this is what I was thinking.

    God I was wrong.

    I usually try to be lazy when this can save me some unnecessary work, but laziness is bad when it comes to testing your application. The morning after, I decided to check my viewDidUnload method implementation, so I fired up gdb and added a breakpoint on it. I was surprised to discover it was never called.

    The reason is simple when you think about it, but is easy to overlook: By calling - [NSNotification removeObserver:] in my viewDidDisappear method, I not only stopped listening to keyboard event notifications, I also got rid of all previous registrations which UIViewController made under the hoods! This includes memory warning notifications, of course, but also device orientation notifications. Had the application I was working on supported more than one orientation (which was not the case), I would also have discovered that my YoYoViewController would not have been able to rotate anymore once it had disappeared.

    The solution to this problem is straightforward: Don't be lazy when unregistering from the notification center, always specify the notifications you stop listening to:
    - (void)viewDidDisappear:(BOOL)animated
    {
        [super viewDidDisappear:animated];
    
        [[NSNotificationCenter defaultCenter] removeObserver:self
                                                        name:UIKeyboardWillShowNotification
                                                      object:nil]; 
        [[NSNotificationCenter defaultCenter] removeObserver:self
                                                        name:UIKeyboardWillHideNotification
                                                      object:nil];
    }
    


    Moral of the story

    Never ever use - [NSNotification removeObserver:] to unregister from notification events, except from a dealloc method. This might cancel registrations made by a parent class, and you cannot know how a parent class is implemented (at least you shouldn't care). Stick to this rule even if your parent class is NSObject: Your class hierarchy might change in the future, and you do not want to run into problems when you don't have to, do you?

    This article discusses - [NSNotification removeObserver:] specifically, but any unregistration / cleanup method being too greedy suffers from the same issues (e.g. - [NSObject cancelPreviousPerformRequestsWithTarget:]). Be careful.

    Use Objective-C properties to manage non-GC resources easily

    In this article, we discuss how Objective-C properties can help you manage resources easily, and how they can be seen as the Objective-C counterpart of C++ smart pointers.

    Smart pointers, who are you?

    C++ programmers are familiar with smart pointers, which are lightweight objects acquiring ownership of a resource, usually a slice of memory allocated from the heap. These objects are also responsible of releasing the resource they manage when they are done with it. The "smart" attribute stems from the fact that, thanks to C++ support for operator overloading, it was possible to design objects that have pointer-like semantics. Such objects can then be used as drop-in replacements for raw C pointers. In fact, smart pointers appeared to be so useful they have been added to the C++0x standard (finally C++11?). Many implementations of smart pointers exist, most notably within the standard library itself (the "gimme-a-punch-in-the-head" std::auto_ptr) or within Boost.

    Smart pointers can exist in C++ thanks to the combination of several language features. First, overloading operator-> and operator* allow to dress up objects with raw pointers clothes. Second, the destructor is the bit of magic by which the responsibility of managing resources can be transferred completely from the programmer to the smart object. Finally, templates are used to generate as many smart pointer flavors as needed to satisfy C++ type constraints. In more advanced implementations (see the wonderful book Modern C++ Design by Andrei Alexandrescu), templates can further be used to design policies allowing to customize a smart pointer behavior (the way it manages ownership, whether it locks resources for concurrent access, etc.)

    Smart pointers and Objective-C: An impossible marriage?

    Among the several smart pointer flavors often implemented in C++, two are of particular interest from the point of view of an Objective-C programmer:
    • Reference-counted pointers, which count how many times an object is referenced by the running program. When the counter variable drops to zero, the managed resource is immediately released. Such pointers represent strong references, and may suffer from retain cycle issues: If two objects each maintain a strong reference to each other, they will ultimately collapse into an unreachable object pair, leading to a memory leak. The same problem can also arise if a chain of objects connected by reference-counted pointers closes itself somehow.
    • Weak pointers, which have been introduced to solve the retain cycle problem. Such pointers contribute to the reference count, but with the special property that objects only referenced through weak pointers are still destroyed. By identifying the head and the tail of an object chain made of strong references, one can break the cycle by having the tail only store a weak reference to the head. In object pairs, this ultimately reduces to the identification of a parent object which will store the strong reference. The other object then only weakly references the parent one. One could use raw pointers to this purpose, of course, but weak pointers objects offer more robustness against the use of an unreleased resource.
    C++ leaves the programer completely free to choose how she wants to manage heap memory, whether through manual allocation and deallocation, or by using smart pointer objects. In Objective-C (we are not talking about Objective-C++ here), no true C++-like smart pointer objects can exist due to the lack of constructor, templates and operator overloading. Nevertheless, non-GC Objective-C still offers a reference-counted strategy for objects allocated on the heap: Objects are identified by a raw pointer to their memory location, but the runtime maintains a reference counter for each of them. In the case of C++ reference-counted pointers, the reference counter is transparently increased when copying or destroying pointers, thanks to the use of C++ destructors and operator overloading. Objective-C does not have any of these features, though, and therefore requires the programmer to explicitly ask the runtime to increment or decrement the counter associated with an object. When the counter drops to zero, the dealloc message is sent to the object and the associated memory is reclaimed.

    The Objective-C runtime therefore frees the programmer from defining a private instance variable for the reference counter and from calling dealloc when the object is no longer in use, but still requires a careful manual management of the reference counter. Failure to do so leads to crashes or memory leaks, features that should not be part of your project backlog.

    Well, this was in fact the situation before Objective-C properties were introduced. In many respects, properties can be seen as the Objective-C counterpart of C++ smart pointers. When assigning an autoreleased object to a property with retain or copy semantics, the programmer is ultimately transferring ownership of the object to the property. When assigning another autoreleased object to the same property, no special care has to be taken since the property will guarantee that the proper semantics is preserved, sending retain, copy or release messages as required. The difference with C++ smart pointers is that the programmer still has to set the property to nil when it is done with the attached object, since no destructor concept is available in Objective-C.

    Rules for using properties as smart pointers

    In order to use Objective-C properties as smart pointers, apply the following rules:
    • For each and every object appearing as an instance variable of a class, create an internal instance variable (in the @private section for tighter encapsulation), naming it using some prefix or postfix of your choice. You should avoid leading underscores since they are reserved for system framework implementations (I personally use m_)
    • For each and every object instance variable, define an associated property with proper attributes (nonatomic, retain, assign, copy, etc.). The name of the property is simply the name of the instance variable without its prefix or postfix. Strong references are obtained by using the retain attribute, weak ones by using assign
    • Never access object instance variables directly, always access them through the associated properties, both for read and write operations
    • There is one exception to the previous rule: Object instance variable can (and must!) only be used in the limited scope of the corresponding accessor and mutator implementations (if @synthesize-ing them does not fulfill your needs)
    • In the dealloc method of your class, set all object properties you have to nil, which will release them. Also set properties with assign semantics to nil, even if it seems unnecessary: If the semantics is later changed to retain, your code will already be correct. Moreover, if you implemented the mutator by hand, you definitely want to call it, don't you?
    • If an object property must be readonly in the public class interface, use a class extension to redeclare a corresponding hidden readwrite property in the non-public implementation file. Note that you have to use extensions, categories won't work (for more information, see Objective-C programming guide). Also use extensions when declaring readwrite properties you do not want to appear in the public interface.
    • If you ever need to implement a property accessor or mutator yourself, be sure its implementation matches the semantics of its declaration
    The benefits of applying these rules are discussed further below. Let me just show you an example first.

    The Magic 8-ball example

    Consider how you would usually design and implement a Magic 8-ball class without properties. Such a class provides you with a shake method to ask for a new nasty message, a rub method to get a new nice message, and a readonly fortuneMessage accessor for reading the message currently displayed:
    // MagicEightBall.h
    
    @interface MagicEightBall {
    @private
        NSString *m_fortuneMessage;
    }
    
    - (void)shake;
    - (void)rub;
    
    - (NSString *)fortuneMessage;
    
    @end
    
    
    // MagicEightBall.m
    
    @implementation MagicEightBall
    
    - (void)dealloc
    {
        [m_fortuneMessage release];
        [super dealloc];
    }
    
    // pick... methods below return autoreleased objects
    
    - (void)shake
    {
        [self playShakeSound];
        [m_fortuneMessage release];
        m_fortuneMessage = [[self pickNastyRandomMessage] retain];
    }
    
    - (void)rub
    {
        [self playRubSound];
        [m_fortuneMessage release];
        m_fortuneMessage = [[self pickNiceRandomMessage] retain];
    }
    
    - (NSString *)fortuneMessage
    {
         NSLog(@"Fortune message read");
         return m_fortuneMessage;
    }
    
    /* ... */
    
    @end
    
    If you ever forget to release m_fortuneMessage before assigning it a new object, your 8-ball will suffer from a memory leak. If you release it too much it will happily blow in your own hands on the spot.

    Now consider how the same class can be implemented using properties:
    // MagicEightBall.h
    
    @interface MagicEightBall {
    @private
        NSString *m_fortuneMessage;
    }
    
    - (void)shake;
    - (void)rub;
    
    // Readonly in public class interface
    @property (nonatomic, readonly, retain) NSString *fortuneMessage;
    
    @end
    
    
    // MagicEightBall.m
    
    @interface MagicEightBall ()
    // Readwrite in hidden implementation file, must use class 
    // extension to redeclare the property
    @property (nonatomic, retain) NSString *fortuneMessage;
    @end
    
    @implementation MagicEightBall
    
    - (void)dealloc
    {
        self.fortuneMessage = nil;
        [super dealloc];
    }
    
    // pick... methods below return autoreleased objects
    
    - (void)shake
    {
        [self playShakeSound];
        self.fortuneMessage = [self pickNastyRandomMessage];
    }
    
    - (void)rub
    {
        [self playRubSound];
        self.fortuneMessage = [self pickNiceRandomMessage];
    }
    
    @synthesize fortuneMessage = m_fortuneMessage;
    
    - (NSString *)fortuneMessage
    {
         NSLog(@"Fortune message read");
         return m_fortuneMessage;
    }
    
    /* ... */
    
    @end
    
    Provided the fortuneMessage property accessor and mutator are correctly implemented (and they are if all you do is @synthesize-ing them), no retain or release now ever need to be sent to m_fortuneMessage, except maybe in the mutator implementation. In fact, if you stick to rules listed above, it should now be impossible to over- or under-release m_fortuneMessage.

    Benefits

    By sticking to the above rules consistently:
    • The semantics of your properties is not scattered through your implementation anymore, but contained in its declaration (this is very similar to what is achieved using policies in C++ smart pointer implementations, see Modern C++ Design). For example, if you later need to change the semantics of a property from retain to copy, updating its declaration (and maybe its redeclaration if you have one) suffices. If the corresponding setter was not @syntesize-d, its implementation also needs to be updated consistently.
    • Access through properties provides you with encapsulation, which is especially good if you later need to update your implementation to perform additional tasks when setting or reading a property. In such cases, manual implementation of the accessor or mutator is required, of course.
    • By adding a prefix or postfix to the object member variables, you avoid using them by mistake. Moreover, this prevents name hiding issues. Usually, those are resolved by adding other prefixes (usually 'a', 'an' or 'the'), but this puts more burden on the programmer. Moreover, this often forces you to edit method prototypes borrowed from header files or obtained using autocompletion, which is counter-productive and error-prone (especially if you disabled name hiding warnings, which you didn't, of course)
    • It is not required to list member variables in the .h, introducing them at @syntesize-ation point suffices. I find this practice especially convenient to have an overview of all variables, though. This is especially useful when checking that a dealloc method is complete, for example.
    • You almost never have to retain or release objects anymore, you can work with autorelease-d objects most of the time. You can still use retain / release pairs to avoid using autorelease locally (e.g. in loops), but this should be the exception, not the rule
    I apply the above rules consistently in all my projects, and I have found that they help me write code which is more robust, easier to write and to maintain. I now rarely need to debug memory management issues like over-releasing objects, and I use properties to design interfaces which are more expressive (since a property declaration defines its semantics). This even saved me enough time to blog about it!

    All is for the best in the best of all possible worlds (really?)

    You can of course argue that:
    • A readwrite property, even hidden in the implementation file, can still be called. Well, we are talking about Objective-C, a world where almost everything is possible. But in such cases the compiler would warn you (you do NOT ignore warnings, do you?).
    • Properties enable KVC and KVO. With KVC, access to hidden properties is made possible (they exist, after all), as discussed just above. With KVO, notifications might also be received when objects are set to nil in dealloc. I personally use KVO with great care, since the best way to make a program workflow difficult to understand is to have notifications thrown around like mad. Most of the time, I prefer to stick with a delegation mechanism, which is far easier to follow. If this still bothers you, you can extend the above rules by allowing prefixed / postfixed object member variables to be used in dealloc as well.
    • The semantics of a property can be seen in the public header file. Knowing that an object is retained or copied for a public readonly property does not really make sense, I must admit (since clients are not supposed to be able to use them for assignment, except if they cheat). But at least it does not hurt.
    • More "dummy" code has to be written (e.g. @synthesize directives, listing instance variables in the header file). Well, that's what Accessorizer is made for, isn't it?


    Conclusion

    Objective-C properties are your best friend. When used correctly and wisely, they will help you write better code, easier to maintain and understand. In the end, you can even manage to write code with almost no explicit retain or release (only autoreleases). Start using properties today, you won't regret it.

    Welcome!

    Welcome to my blog, fellow programmer! Fasten your seatbelt and enjoy the ride!

    This blog will be dedicated to Objective-C world as seen from the humble point of view of an iOS developer (therefore its name). It will mostly focus on best practices, design issues and low-level stuff, since these are my primary interests. Feel free to comment, pinpoint inconsistencies or just share your opinion, all this Objective-C stuff is highly subjective, after all!

    Happy reading!