Tuesday, September 1, 2009

iphone tutorial: Memory management - autorelease and NIB files

In this tutorial we're going to dive a bit deeper into Cocoa memory management, specifically looking at a feature called autorelease. If you haven't read the first part of the memory management tutorial, it's highly recommended that you do.


Cocoa memory ownership rules


In the first part of this tutorial we introduced the concept of memory ownership. To help deciding who actually owns a memory block, the Cocoa designers have developed a set of rules that all Cocoa classes and applications should follow. This set of rules can be found in the "Memory Management Programming Guide for Cocoa":


---

To make sure it is clear when you own an object and when you do not, and what responsibilities you have as an owner, Cocoa sets the following policy:


You own any object you create.

You “create” an object using a method whose name begins with “alloc” or “new” or contains “copy” (for example, alloc, newObject, or mutableCopy).


If you own an object, you are responsible for relinquishing ownership when you have finished with it. You relinquish ownership of an object by sending it a release message or an autorelease message (autorelease is discussed in more detail in “Delayed Release”). In Cocoa terminology, relinquishing ownership of an object is typically referred to as “releasing” an object.


If you do not own an object, you must not release it.

---


If you think these rules are too complicated to remember, try to at least remember the naming scheme; methods beginning with "alloc" or "new" or contains "copy" creates an object (and returns a reference to that object) that you are responsible for freeing. That is, these methods, transfer the ownership to you, the caller of the moethod.


A new concept is also introduced; autorelease or "delayed release". If you call 'autorelease' on an object reference, that object won't be released immediately, but "later". Before we explain what later means, it might be good to understand why there is such a feature as a "delayed release".


Autorelease


Why would you want to release an object later instead of now or not at all? One example is when you have a method that creates objects for someone else than itself.


(MyClass.m)

+(MyClass *)createObject {

MyClass *reference = [[MyClass alloc] init];

return reference;

}


The method 'createObject' in the class MyClass allocates and initialises a MyClass object and then returns the reference to the newly created object. An application calling this method


(MyApplication.m)

MyClass *myClass = [MyClass createObject];


would not be responsible for releasing the object since the 'createObject' method name doesn't contain any of the "magic words" listed in the rules above. The 'createObject' method on the other hand calls 'alloc' which is a magic word and is therefore responsible for calling release.


It's impossible to call 'release' on the created object after the return statement and 'release' is called before the return statement, the object will be deallocated and the method will return a reference to a deallocated object which will lead to chaos. So when should 'release' be called? Well, "later"...


Here "later" means "after the return statement" and late enough so that the caller of the method gets a chance to store the returned release in a variable (or somewhere else) and call 'retain' on it.


This is where the autorelease mechanism steps in and provides a solution. If the method creating the object calls 'autorelease' on the reference instead of 'release' evertyhing will work; the method has fulfilled its responsibility to release the object it created and the caller of the method gets a chance to call retain on the returned object.


(MyClass.m)

+(MyClass *)createObject {

MyClass *reference = [[MyClass alloc] init];

[reference autorelease]; // call autorelease on the newly create object

return reference;

}


(MyApplication.m)

MyClass *myClass = [MyClass createObject];

[myClass retain]; // call retain on the returned object to protect it from being deallocated


The autorelease method can be called anywhere the release method should have been called but just as with release it's important to call it the right number of times; for each time 'autorelease' is called 'release' will be called "later".


NIB files


We mentioned above that a method that creates an object for someone else is a typical user of the autorelease functionality. Another user of autorelease is a NIB file, or actually the method that load it. A NIB file contains a graph of objects where most objects are connected to each other using outlets. When the loader method rebuilds this object graph in memory, using the NIB file as a blueprint, it creates all objects with a reference count of 1 and then autorelease them.


When the connections between the objects are re-established, the loader method calls setter methods of the objects, which should retain the object reference if they want to prevent the newly loaded object from being deallocated. Ownership is transferred to these objects, meaning that the objects also are responsible for releasing the references in their 'dealloc' method. Setter methods for outlets are normally synthesized using the @property/@synthesize mechanism of Objective C.


@property (nonatomic, retain) IBOutlet someClass *someOutlet;


The 'retain' keyword of the property declaration will ensure that the corresponding setter method created by the @synthesize keyword will retain the reference passed to it.


However, there are also top-level objects in the file that have no natural owner. If you want to keep those objects around, which you normally do, you have to manually retain them (and then later manually releasing them). But how do you get hold of the references to them if they have no "owners"? Well, they do actually get a temporary owner during the loading process; references to all the top-level objects are stored in an array, returned by the 'loadNibNamed:owner:options' method.


As a simple alternative to iterating through the array and retain all the objects in it individually you could retain the array itself instead (and then later release it). This works since it prevents the array from being (auto) released and deallocated - if it was it would release all the references stored in it (decreasing the reference count to zero) and thus force all the objects in the array to be deallocated.


NSArray *nibArray = [[NSBundle mainBundle] loadNibNamed:@"Nib1" owner:self options:nil];

if ( nibArray == nil ) {

NSLog(@"loadNibNamed failed");

return;

}

[nibArray retain];


NIB files with "unconnected" top-level objects aren't that common since it would be useless to define a lot of objects in a NIB that no one uses. Much more common is that the top-level objects are connected to outlets in the "File's owner" object. When that is the case you don't have to worry about implicitly retaining the objects since they will be retained by the setter methods in the "File's owner".


Monday, August 10, 2009

iPhone tutorial: Memory management - the basics

In this tutorial I will try to explain how to manage dynamically allocated memory in Objective C, or more specifically in Cocoa, which is the object oriented programming environment ("API") used for most iPhone applications.


I will start with a long introduction to general memory management so if you already know everything about that, feel free to skip to the Cocoa specifics. The reason for the long introduction is that it's memory management in general that is complex, not the particulars of Cocoa memory management.


A long introduction to general memory management


When you need a block of memory for something in a computer program you normally allocate it by making a call to a memory manager. The memory manager might be provided by the operating system, a library, a framework, or something similar. When you don't need the allocated block of memory any longer you normally free it - also by calling the memory manager - to make it available for allocation again.


In most languages with manual memory management, that is, where you, the programmer, has to decide when to allocate and free memory there is one thing you have to remember: free the memory that you allocated! If you forget to free a block of memory, the computer will sooner or later run out of memory, which will cause most programs to stop function properly. Forgetting to free allocated memory is often referred to as leaking memory.


In simple programs, it's quite easy to decide when to allocate and when to free memory, since these programs usually do things in a linear fashion; you allocate memory, do some processing and then free it.


memoryBlock = allocateMemory(10); // allocate a 10 byte big memory block

process(memoryBlock); // do some processing

free(memoryBlock); // free the allocated memory block


However, as the complexity of a program increases, you might have to keep the allocated memory around for longer periods of time and the linear chain of events (allocate, process, free) might be broken. Furthermore, you will probably have to allocate multiple blocks of memory and perhaps free them in a different order than the one you allocated them in.


When you start interacting with code you don't control yourself (libraries, classes, operating systems, frameworks, etc.) things can get complicated even if the underlying principle is very simple (free what you allocated). It can get especially nasty when you allocate a block of memory and then "give it away to someone else" or if you get a memory block from someone. Who should free that memory block and, just as importantly, when should it be freed? To answer those questions, there has to be a way of deciding who owns a block of memory and/or when it is not needed any longer - something we will dive deeper into later in this tutorial.


Addresses and pointers


When you allocate a memory block from a memory manager you normally get the address of the memory block as the result. The address is simply an offset into the computer's (or program's) memory space, where each byte has a unique address starting from 0 (zero) and increasing upwards.


Let's say that we have a very simple computer system, with only 256 bytes of memory. That is, there are memory address ranging from 0 to 255. If we start by allocating 10 bytes of memory, you would get the address 0 (zero) as the result. If you then allocate 20 bytes you would get the address 10 (0 + 10) as the result since addresses 0 to 9 are already allocated. Allocating yet another 30 bytes would get the address 30 (10 + 20), and so on.


(Memory address: Memory block size)

0...9: 10 bytes

10...29: 20 bytes

30...59: 30 bytes


If you store the address returned by the memory manager in a variable, that variable is often called a pointer, since it "points to" a memory block. In most languages you normally specify that the variable is going to be used as a pointer, since this will allow you to perform "pointer operations" on that variable. It will also enable sanity and error checks. Furthermore, if you can inform the language what type of information the memory block will contain - a number, a string, a specific data structure, an array of integers, etc. - you often get access to even more operations and error checking.


Note that if you don't store the address of the allocated memory block in a variable (or somewhere else) it will be impossible the free the memory block, and you will leak memory!


It's important to understand that a pointer isn't something magical - it's just a value! This means that you can use pointers anywhere you can use variables and values; passing them as arguments to a function or a method for example. You can also create copies of pointers without any strange side effects - it won't copy the memory block! - all that happens is that you now have two variables pointing to the same memory block.


pointer = allocate(10); // allocate a 10 bytes big memory block and store the address in a variable called 'pointer'

pointerCopy = pointer; // assign the value (address) stored in 'pointer' to a variable called 'pointerCopy'


If you want to free the allocated memory block you can do it either by calling free(pointer) or free(pointerCopy) since they both point to the same memory block, but it's very important to free the block only once! Calling both free(pointer) and free(pointerCopy) could have catastrophic consequences! Therefore, once you start copying pointers or passing them as arguments to functions or methods you have to be very careful to ensure that the memory is freed only once!


Ownership of dynamically allocated memory


The simplest way of deciding who should free a block of memory and when is to let the owner of the memory block decide it. In simple programs, the code allocating the memory block is the owner, but in more complicated programs ownership of a memory block can be transferred between different parts of the system (library, operating system, framework).


In some cases it's obvious who should free the memory block. For example, if you have an arrangement where a producer task allocates memory and hands it over to a consumer task, the consumer will free the memory when it is done with it. The same arrangement is often seen when a program interacts with a library or a framework. The library or framework allocates memory for you, but it's your responsibility to free it. In both these arrangements, ownership of the memory block has been transferred from the one that allocated it to someone else.


But what happens if the producer would like to keep the memory block around for its own use after passing it to the consumer? Or what if the producer wants to send the same memory block to a logging task as well as to the consumer task? In a situation like this, the consumer and logger are probably not aware of each other and therefore both would think that they would be responsible for freeing the memory block given to them by the producer.


producer: pointer = allocateMemory(10)

producer: process(pointer)

producer: sendToConsumer(pointer)

producer: sendToLogging(pointer)

...

logger: pointer = getMemoryBlockFromProducer()

logger: process(pointer)

logger: free(pointer)

...

consumer: pointer = getMemoryBlockFromProducer()

consumer: process(pointer)

consumer: free(pointer)

...


If the logger is executed before the consumer, like in the scenario above, the memory block would already be freed by the logger once the consumer gets it. This is catastrophic since anything could have happened to that memory block after it was freed. The worst scenario is that someone else could have allocated the same memory block and changed the contents of it.


The problem here is that it is not clear who owns the memory block - the producer, consumer or logger - and therefore it is not clear who should free it. In fact, one can say that there are multiple owners of the memory block!


Copying memory to avoid ownership problems


A simple way of avoiding ownership problems is to ensure that each memory block only has one owner and that that owner is responsible for freeing it. This can be achieved by allocating a new memory block, copy the contents of the original block into the new block and then hand over the copy to the one needing it.


producer: pointer = allocateMemory(10) // allocate a 10 bytes memory block

producer: process(pointer) // do some processing


producer: pointer2 = allocateMemory(10) // allocate a second 10 bytes memory block

producer: copyMemory(pointer2, pointer) // copy the contents of the first memory block into the second memory block

producer: sendToConsumer(pointer) // send the second memory block to the consumer


producer: pointer3 = allocateMemory(10) // allocate a third 10 bytes memory block

producer: copyMemory(pointer3, pointer) // copy the contents of the first memory block into the third memory block

producer: sendToLogging(pointer3) // send the third memory block to the logger

...


producer: free(pointer) // free the first memory block since we are the owner of it


The consumer and logger both free their respective memory blocks once they are done with it. This works very well, but there are two things to consider; more memory is used since we make a lot of copies and a lot of time is wasted with copying the information from the original block into the copy, which will make the program run slower. On modern computers with plenty of memory and fast CPUs, this is probably no catastrophe, but still a little wasteful. On the other hand, the wastefulness might be a cheap price to pay for avoiding memory leaks or other nasty memory related problems!


Another thing to consider is that the information in the memory blocks (the original and the copies) start "living their own lives", that is, when the information in the original block is changed the information in the copies remains unchanged and vice versa. In some situations this doesn't matter or might even be required, but in others it is catastrophic. The computer doesn't know which behaviour you want/require, so you as the programmer need to inform the computer by writing code that produces the required behaviour.


Copying memory that contains pointers


We have previously hinted that a pointer doesn't have to be stored in a variable - it can also be stored "somewhere else". This other place is normally in a memory block. We have emphasized that a pointer is just a value, but we haven't mentioned anything about what type of value it is. It's easiest to consider a pointer to be a positive integer with a value between 0 (zero) and some maximum value. The simple computer system used above only has 256 bytes of memory, that is addresses between 0 and 255, which means that the maximum value for a pointer in this system is 255. This, in turn, means that a pointer can be represented using a single byte.


Deep and shallow copies


Let's say we have the following arrangement. We allocate three 1-byte memory blocks, starting at address 50 and fill the blocks with the integers 1, 2 and 3 respectively. After that we allocate a 3-byte memory block at address 53 (50 + 3) and fill the block with the values 50, 51 and 52.


(Address: Memory block size: Contents)

50: 1 bytes: 1

51: 1 bytes: 2

52: 1 bytes: 3

53: 3 bytes: 50, 51, 52


What we have created in the 3-byte memory block is effectively an array of integers, but instead of containing the integers themselves, the array contains pointers to the integers.


What if we want to make a copy of this array? If the array had contained the integers themselves, there wouldn't be much to think about; just allocate a second 3-byte memory block and copy the contents of the first 3-byte memory block into the second. But now the array contains pointers to the integers instead of integers, so if we try the simple copy just described we would end up with the following:


(Address: Memory block size: Contents)

50: 1 bytes: 1

51: 1 bytes: 2

52: 1 bytes: 3

53: 3 bytes: 50, 51, 52

56: 3 bytes: 50, 51, 52


What happens is that we have copied the pointers. Nothing else. This is similar to what we did above when we copied a variable containing a pointer. Just as above, we now have two pointers pointing to the same memory block. If we change the integer at address 50 from 1 to 10, this change will be "seen" by both the original array and the copy.


Sometimes this is what one needs or requires, but sometimes a "real" - fully independent - copy is required, that is, a copy which won't be affected by changes in the original.


To accomplish this, we need to copy the three 1-byte memory blocks instead of the 3-byte memory block containing the pointers. That is, we have to allocate three new 1-byte memory block and copy the contents of the first three 1-byte memory blocks into the three new ones. After that we have to allocate a second 3-byte memory block and put the addresses of the three new 1-byte memory blocks in it.


(Address: Memory block size: Contents)

50: 1 bytes: 1

51: 1 bytes: 2

52: 1 bytes: 3

53: 3 bytes: 50, 51, 52

56: 1 bytes: 1

57: 1 bytes: 2

58: 1 bytes: 3

59: 3 bytes: 56, 57, 58


Now we have made a truly indepent copy of the array. This is called making a deep copy. Just copying the pointers as we did initially is called making a shallow copy. If we now change the integer at address 50 from 1 to 10, this will only be "seen" by the original array, not the copy.


As mentioned above, the decision to make a deep or shallow copy can only be made by the programmer, since the computer doesn't know what kind of behaviour your require.


Reference counting


As we have mentioned before, it's quite common to get into situations where a memory block has several owners or users. We have also mentioned that it's hard to decide when and who of the owners that should free such a block. A technique called reference counting solves both these problems. Provided that all the owners play by the same rules, that is.


A simple set of rules that is quite easy to follow and also quite easy to implement in the memory manager is a technique called reference counting. As the name implies, the technique works by counting how many references (pointers) there are to a certain memory block, that is how many owners the block has.


When the memory block is first allocated, by the first owner, the counter is set to 1. If the first user "gives away" the block to someone else, for example by passing it to another task, the counter is increased by one 1 to signal that there now are two owners of the block - or two references to it.


When any of the owners frees the block, the counter is decreased and when it reaches zero it is freed. If the counter is greater than zero, nothing happens! This means that the memory block is "kept around" until all users are done with it - very simple and very efficient!


In normal memory management all you do is allocate memory, passing and copying pointers, and freeing memory. The only thing that is different from "normal" memory management when you use reference counting is that someone actively has to increase the reference counter. Also, you won't be able to call the normal free function when you're done with a memory block; you instead have to call a special kind of free that decreases the reference counter and don't call the normal free until the counter reaches zero.


To summarise, when you use reference counting you have to inform the computer when you want to add an owner (increase the counter) or remove an owner (decrease the counter) to a memory block. When it's appropriate to do this is completely up to the programmer to decide since the computer has no way of knowing what type of behaviour you want, and that is where the complexity of reference counting lies - calling the add/remove owner functions at the right time and place.


So how could reference counting be implemented by the memory manager? A very simple solution is to allocate a little more memory each time someone requests memory and use this extra memory to hold the reference counter for the memory block. In our simple 256-byte computer, this could mean allocating one extra byte for each memory block. If some requests 3 bytes of memory, the memory manager actually returns 4 bytes of memory but instead of returning the address to the first byte in the memory block, it returns the address to the second byte.


50: 1 (this address holds the reference counter and is initialised to 1 by the memory manager)

51: ??? (this is the address returned by the memory manager)

52: ???

53: ???


When someone calls the "remove owner" function on the memory block above, the argument to the function will be a pointer containing the value 51 (not 50!). However, the function knows that there actually is an extra byte stored before the memory block, so it decreases the value stored in the extra byte by one and checks if it becomes zero (no more owners left). If it reaches zero, the 4-byte memory block is freed "for real."


The best thing with this very simple solution is that it can be implemented "on top of" a normal, non-reference counting, memory manager. This makes it possible to write a reference counting memory manager in a library (or even in the application itself!). After that you could start writing applications that use the library and these applications would then get support for reference couting memory management even though the underlying memory manager (for example an operating system) doesn't support it.


So how big an impact does reference counting have on the code that you write? Do you have to start thinking in completely new ways to use it properly? Well, you do have to start using at least two new function calls; one for adding an owner to a memory block and one to remove an owner from a memory block. You also have to starting thinking a little differently since you need to call these two functions at appropriate places in the code.


In an example above (in the section about copying memory blocks to avoid ownership problems), we had the producer copy a memory block before giving it to the consumer and logger respectively. Could we have made it the "other way round", that is, having the consumer and logger copy the block given to them? Yes, that would have worked just as well! Who should make the copy is just a matter of perspective, that is, who should be aware of the ownership problems - the giver or the receiver?


The same reasoning applies to reference counting, but instead of copying memory blocks an owner is added to a memory block. Adding an owner can be made either by the giver or the receiver. However, often it's "nicer" or more convenient to have the receiver add itself as an owner instead of having the giver add the receiver as an owner. Later, when the receiver is done with the memory block it can remove itself as an owner. That way, the memory management issues can be completely "contained" in the receiver (application, class, library, framework, etc.).


Objective C memory management


In object oriented languages like Objective C, memory is usually allocated in order to create an object. Therefore, one usually talks about objects instead of memory blocks and references to objects instead of pointers even though a reference is nothing else but a variable containing the address of an object. Just as with pointers, references are normally "typed" to inform the language what type of object that is "referenced", but there is also a general reference type called 'id' which can reference any type of object.


As with all posts in this blog, remember that I am writing about stuff I have recently begun to understand myself and this is particularly true for the memory management parts of Objective C and Cocoa. That said, as far as I understand, Objective C doesn't have any standardised memory management scheme apart from functions malloc() and free from the standard C library.


Cocoa memory management


Cocoa can be explained as a "programming environment", API or class library intended to radically simplify the development of complex (graphical) applications. In this tutorial, we're going to focus on what it has to offer when it comes to memory management.


Cocoa uses reference counting to manage memory ownership problems and it's usually the receiver of a reference that "adds itself as an owner" to an object. In Cocoa, the reference counting scheme is accessed through the Cocoa root class NSObject, or actually the NSObject protocol. Since almost all objects in Cocoa implement this protocol, the reference counting scheme is available to "everyone". Furthermore, a set of rules have been developed by the Cocoa developers, meaning that almost all Cocoa classes are compatible with each other with regard to memory management. These rules also simplifies the life for the application developer (you!).


Allocating an object


When you want to allocate memory for an object you call the 'alloc' method of the class.


MyClass *myClass = [MyClass alloc];


This line of code first declares an object reference called 'myClass' of the type 'MyClass *'. The asterisk (*) is a syntax inherited from the C language, where it declares a variable as a pointer. In this case a pointer to an object of the type MyClass.


[MyClass alloc] sends the message/calls the method 'alloc' of the class MyClass, which allocates a sufficient amount of memory for an object of the type MyClass. It also sets the reference counter of the memory block used for the object to 1 (one) to indicate that there now is one owner associated with the memory - the one calling 'alloc'.


Initialising an object


Before an object can be used it has to be initialised, which normally is done right after memory has been allocated for the object. Initialising an object puts it into a known initial state; assigning default values to member variables, etc. You do this by calling the 'init' method of the NSObject protocol.


MyClass *myClass = [MyClass alloc];

[myClass init];


Note that 'init' is called on the object 'myClass' and not the class 'MyClass', that is, it uses the an object/instance of the type MyClass rather than the class MyClass itself.


Init also returns an object reference (pointer) and since an 'init' method is allowed to return another object than the one it was called on, it's very important to use the pointer returned by init:


MyClass *myClass = [MyClass alloc];

myClass = [myClass init];


Since both 'alloc' and 'init' return an object reference and you have to use the one returned by 'init', it's often more convenient to use "cascading" method calls to allocate and initialise an object with a single statement.


MyClass *myClass = [[MyClass alloc] init];


Freeing an object


When you are done with an object you need to free it, or actually "remove yourself as an owner" as Cocoa uses reference counting rather than manual memory management. You do this by calling the 'release' method of the NSObject protocol.


[myClass release];


This method decreases the reference counter of the memory associated with the object and if the counter reaches zero, the memory is freed. However, before the memory is freed, the 'dealloc' method of the class is called to make it possible for the object to "clean up" before it's freed. Cleaning up basically boils down to releasing all references held by the object. If this step is skipped, memory will leak since no one will take care of releasing the references held by the freed object since it no longer exists!


Adding an owner to an object


When an object should be shared/used by multiple users, the additional users need to add themselves as owners. This is done by calling the 'retain' method of the NSObject protocol.


MyClass *myClass = [[MyClass alloc] init]; // allocate and initialise an object

[myClass retain]; // add an owner (increase the reference counter)

MyClass *myClassReferenceCopy = myClass; // copy the reference


Now the same object has two references; myClass and myClassReferenceCopy. Furthermore, the object has a reference count of two indicating that it has two owners (or references). Conveniently, 'retain' returns a reference to the object, which makes it possible to perform the operations above in two statements instead of three.


MyClass *myClass = [[MyClass alloc] init]; // allocate and initialise an object

MyClass *myClassReferenceCopy = [myClass retain]; // retain the object and copy the reference


Balancing retain and release


A very important rule with reference counting is to balance the addition and removal of ownership, or in Cocoa terms, balancing the calls to retain and release. Balance means that for each call to 'retain' a call to 'release' is required. If this balance isn't maintained, the reference counter of some object will never reach zero and thus will never be deallocated. This effectively creates a memory leak!


Setter methods


One of the most common scenarios for calling 'retain' on a reference is in a "setter" method of a class, that is, when a user of an object wants to assign a value to one of the objects member variables. For example, say that we have two classes A and B, where objects of type A contains a reference to an object of type B.


(ClassA.h)

@interface ClassA : NSObject {

ClassB *classB;

}


-(void)setClassB:(ClassB *)reference;


Here we see that there is a member variable called 'classB' which is a reference to an object of type ClassB. If an application allocates and initialises one instance of ClassA and one instance of ClassB and then call 'setClassB' on A, the reference to the ClassB object will be "given to" the ClassA object.


(MyApplication.m)

ClassA *classA = [[ClassA alloc] init];

ClassB *classB = [[ClassB alloc] init];

[classA setClassB:classB];


A naive implementation of a setter method which simply assigns the value of the supplied parameter to the member variable


(ClassA.m)

-(void)setClassB:(ClassB *)reference {

classB = reference; // store the new reference in a member variable

}


does indeed work in simple situations, but relies on the application not to release the reference it supplied as a paramter to the setter method. If it does, the ClassB object will be deallocated and the ClassA object will have a reference to a deallocated object, which is a catastrophe!


The ClassA object needs to be sure that the ClassB object it is using doesn't get deallocated and to guarantee this it needs to add itself as an owner.


A somewhat less naive implementation is to retain the reference passed as an argument


(ClassA.m)

-(void)setClassB:(ClassB *)reference {

[reference retain]; // retain the new reference

classB = reference; // store the new reference in a member variable

}


So, what is wrong with this implemenation? Well, what happens if setClassB is called more than once? It will lead to a situation where the 'retain' calls aren't balanced by 'release' calls. To sort this out, we have to ensure the setClassB method maintains the balance.


A quite good implementation which maintains the balance between retain and release


(ClassA.m)

-(void)setClassB:(ClassB *)reference {

[classB release]; // release the old reference

[reference retain]; // retain the new reference

classB = reference; // store the new reference in a member variable

}


So, why is this implementaion just "quite good"? What else needs to be done but balancing the 'retain' and 'release' calls? Well, there is a fringe case that isn't handled by that implementation. A case that can be triggered by calling the setter multiple times with the same reference! Doing this could lead to a situation where the object referenced by the 'reference' parameter is deallocated before 'retain' is called on it in the setter.


(MyApplication.m)

ClassA *classA = [[ClassA alloc] init]; // classA reference count == 1

ClassB *classB = [[ClassB alloc] init]; // classB reference count == 1

[classA setClassB:classB];

[classB release]; // release the old reference (nil; nothing happens)

[reference retain]; // retain classB; reference count == 2

classB = reference; // store classB in a member variable

[classA setClassB:classB];

[classB release]; // release the old reference (classB; reference count == 1)

[reference retain]; // retain classB; classB reference count == 2

classB = reference; // store classB in a member variable



Hmm, looking at the 'release' calls in 'setClassB' we see that the reference count temporarily reaches 1 before going back to 2 again, but we never see it reaching zero and then be deallocated.


What saved us this time is that the application still is an owner of the object, and that's why we only reached 1 instead of 0. But doesn't the application has to own the object in order to supply a reference to it as a parameter to 'setClassB'? Not necessarily, all it needs is the reference to the object - it does not have to own it. To protect us from this fringe case we need to reorder the operations in the setter method a bit so that 'retain' is called before 'release'. This will temporarily increase the reference count instead of temporarily decrease it when the setter is called with the same reference multiple times. Temporarily increasing the reference counter is risk free since nothing happens behind the scenes when you do it, in contrast to what can happen when you temporarily decrease.


A perfect implementation of a setter method


(ClassA.m)

-(void)setClassB:(ClassB *)reference {

[reference retain]; // retain the new reference

[classB release]; // release the old reference

classB = reference; // store the new reference in a member variable

}


Getter methods


Getter methods for member variables that are object references can usually be made quite simple since it's customary to let the one calling the getter be responsible for managing the reference counting (calling 'retain' if it wants to keep it around and later, when it is done with it, calling 'release' on it).


(ClassA.m)

-(ClassB *)getClassB {

return classB; // return the reference without calling 'retain' on it

}


(MyApplication.m)

ClassB *classB = [classA getClassB];


[classB retain];

...

[classB release];

Cleaning up


As we mentioned earlier, if a class has member variables that are references to other objects, objects of that class need to "clean up" before being deallocated. We also mentioned that cleaning up often boils down to releasing references that had previously been retained.


In Cocoa, the cleaning up method 'dealloc' gets called on an object when it is time to deallocating the object, so if you have any references retained that's the place to do your class specific cleaning up.


A broken implementation of a dealloc method


(ClassA.m)

-(void)dealloc {

[classB release]; // release the reference that was retained in 'setClassB'

}


Why did we call this implementation broken? What more could possibly be required from us than that we clean up our own mess? Don't worry, there's no more boring household chores for you to do, but you do have to tell your superclass that it should clean up its mess ;)


A perfect implementation of a dealloc method


(ClassA.m)

-(void)dealloc {

[classB release]; // release the reference that was retained in 'setClassB'

[super dealloc]; // tell your superclass that it is time to clean up

}


An alternative implementation of a dealloc method


(ClassA.m)

-(void)dealloc {

[setClassB nil]; // "unset" the reference by setting it to nil

[super dealloc]; // tell your superclass that it is time to clean up

}


By calling the setter method instead of doing an explicit call to 'release' we do all the memory management of the reference in the setter method. Since memory management is complex it's a good idea to do as little as possible of it.


Summary


This tutorial is very long, but if you understand all of it you are well equipped to understand the more complicated parts of memory management - in general and in Cocoa. Basic understanding of dynamic memory management and pointers is important for most types of software development, even though some languages claim that you don't have to worry about it. The time spent on understanding it is therefore usually a good investment for the future.


If there is one thing you should remember from this tutorial it is: free what you allocated, or in Cocoa terms, release what you retained (or allocated).


The title of this post indicates that there will be several parts of this tutorial and those will probably deal with stuff like autorelease pools and deep and shallow copying in Cocoa, and probably some words on how to do memory management when you're dealing with objects created from NIB files.


As always, feel free to suggest improvements and point out errors!

Friday, May 29, 2009

iPhone tutorial: Creating table cells in Interface Builder





Creating complex table view cells programmatically can be quite tedious. In fact, so tedious that it can affect your creativity negatively. Thankfully, it is possible to design table view cells in Interface Builder (IB) and then use them in your application and that is what we're going to explore in this tutorial.

While designing table view cells in IB is quite simple and intuitive, using them in an application is far from intuitive. Especially if you want to use   a single table view cell for multiple (all?) rows in a table. The problem with using a cell multiple times is that you have to create multiple instances of the UITableViewCell object, which in turn means that you have to load the xib containing the object multiple times. This means that you have to create a reusable IB file (xib/nib).

How many instances do you need to create? Well, that is basically decided by the number of visible rows in the table view. Each visible row needs its own instance of a corresponding UITableViewCell object. The cell reuse scheme really won't kick in until you start scrolling the table view, so if you have a table view with 10 visible rows and 20 total rows, you will normally have to create 10 instances of the cell object. This is nothing you should rely on or try to exploit since it's the UITableView object that decides exactly how many instances you need of a specific cell. It does this by returning nil when you call 'dequeueReusableCellWIthIdentifier', which basically is an order to create a new instance - simple as that.

This tutorial could be seen as "part 4" of the "UITableView from the ground up", but I decided against it since it is more or less independent - focusing just on how to load and reuse UITableViewCell objects from an IB file. Therefore, we're going to create a new project instead of modifying the one we created in part 1.

Create the project

Start XCode and choose "File/New Project" from the menu to create a "Window-Based Application" and name it "TableCellLoader". We're going to create a few classes right away, so  select the Classes-group in the "Groups & Files" panel i XCode. Then choose "File/New File" from the menu and create a NSObject subclass called "CellOwner.m" (remember to check the "Also create h-file" checkbox). After that, choose "File/New File" again and create a UITableViewCell subclass called "Cell1" and again to create yet another UITableViewCell subclass, this time called "Cell2".

Classes/Cell1.h

The UITableViewCell we're going to create in IB will be represented by an UITableViewCell subclass in our application. We're actually going to create two similar cells in IB - "Cell1" and "Cell2" - which will only differ in regards to the layout of their contents. The cell content is very simple - two UILabels which will allow us to display two strings. Since the cells are so similiar Cell2.h will be identical toll Cell1.h - with the exception of the name of the class.

In order to access the two labels in the table view cell, we need to define two instance variables - "label" and "label2" - containing pointers to UILabel objects. Since we'll be manipulating them from IB, we also need to make them into properties and mark them with IBOutlet. Those changes should result in the following:

@interface Cell1 : UITableViewCell {
UILabel *label;
UILabel *label2;
}

@property (nonatomic, retain) IBOutlet UILabel *label;
@property (nonatomic, retain) IBOutlet UILabel *label2;

Classes/Cell1.m

Since we created this file as a subclass of UITableViewCell it will already contain some code, but ignore that for now since all we need to do right now is to synthesize the properties we created in the h-file. Since As we mentioned above "Cell1" and "Cell2" are very similar so apply the same changes to the Cell2.m.

All we need to do is to add two @synthesize statements right after the @implementation statement:

@implementation Cell1
@synthesize label;
@synthesize label2;

Classes/CellOwner.h

The CellOwner class will be used to load UITableViewCell objects from IB (xib/nib) files and the rather strange name was chosen because the CellOwner class will be set as the "File's owner" in the IB files for the UITableViewCell objects ("Cell1" and "Cell2").

Since this class is just some kind of "support" class for the IB object loading procedure it doesn't contain much or do much. All it contains is a pointer to the UITableViewCell subclass that is loaded from the IB file and a method which loads an IB file. Therefore the h-file will be quite simple:

@interface CellOwner : NSObject {
UITableViewCell *cell;
}

@property (nonatomic, retain) IBOutlet UITableViewCell *cell;

- (BOOL)loadMyNibFile:(NSString *)nibName;

Classes/CellOwner.m

Since we defined a property in the h-file, we - as always - need to add a corresponding @synthesize statement in the m-file, so add the following right after the @implementation statement:

@synthesize cell;

In the h-file we also declared a method which we need to implement in the m-file, so add the following:

- (BOOL)loadMyNibFile:(NSString *)nibName {
    // The myNib file must be in the bundle that defines self's class.
    if ([[NSBundle mainBundle] loadNibNamed:nibName owner:self options:nil] == nil)
    {
        NSLog(@"Warning! Could not load %@ file.\n", nibName);
        return NO;
    }
    return YES;
}

The source code for this method was taking more or less directly from an example in the "Resource Programming Guide" which you can find by searching for "loadMyNibFile" in the API docs in XCode (remember to select "Full-Text" in the upper left corner of the API docs window.

As you can see, it's quite simple to load an IB file - it's basically just one line of code! The rest of the code is error handling. To keep up the pace of this tutorial we won't dive into the details of the NSBundle class, so if you want to know more about that right now, please search for it in the API docs.

'loadNibNamed' takes three arguments; the name of the nib (IB) file to load, the owner of the file ("File's owner" in IB) and something called 'options'. As we said above, our single CellOwner object will be the "File's owner" of all the cells we load, which explains why we pass 'self' as the value of the 'owner' argument. The 'options' argument is only used if the IB file we load contain any non-standard "proxy objects". We don't use this feature and thus we can pass 'nil' as the value.

Classes/TableCellLoaderAppDelegate.h

Our application delegate will contain a reference to an object of the CellOwner class we created above, so add an instance variable and a property for it as well as marking it as IBOutlet since we'll create it in IB. After you're done, the file should look like this:

@interface TableCellLoaderAppDelegate : NSObject {
   UIWindow *window;
   CellOwner *cellOwner;
}

@property (nonatomic, retain) IBOutlet UIWindow *window;
@property (nonatomic, retain) IBOutlet CellOwner *cellOwner;

Classes/TableCellLoaderAppDelegate.m

This is the file we'll keep adding code to during the tutorial but right now we'll just add the @synthesize statement corresponding to the property we added in the h-file ("cellOwner"). So add the following just below the already existing "@synthesize window" statement:

@synthesize cellOwner;

Test build

Build the project in XCode (CMD-B) to verify that everything works. There should be no warnings or errors reported.

Resources/MainWindow.xib

Double-click on Resources/MainWindow.xib to start IB and load the file. As always, remember to switch to "hierarchical view mode" by pressing the middle button above the "View Mode" text in the upper left corner of the MainWindow.xib window.

We need a table view in order to be able to experiment with table view cells, so let's add one to our window. Open the Library window in IB (CMD-L) and drag a "Table View" object from the "Data Views" section and drop it onto the Window object in the MainWindow.xib window.

A table view needs the help of two other objects to function properly - a 'delegate' and a 'dataSource' - so we'll need to connect those outlets of the the "Table View" object we just added. CTRL-drag from "Table View" to "Table Cell Loader App Delegate" and choose 'delegate' from the window that pops up. Repeat the process for the 'dataSource'.

We're going to use a single instance of our "CellOwner" object to load our UITableViewCell objects from IB files, so let's create that one as well. Drag a "Object" object from the "Controllers" section of the Library window (CMD-L) and drop it at the end of the list in the MainWindow.xib window. 

Select the "Object" object in MainWindow.xib and press CMD-4 to bring the Inspector window to the front and select the Identity tab. Here you should change the class of the object to "CellOwner" in the drop-down list. Remember that we added a "cellOwner" property to our application delegate? Now is the time to connect it, so CTRL-drag from "Table Cell Loader App Delegate" to "Cell Owner" in MainWindow.xib and choose "cellOwner" in the pop-up window that appears.

We're done with MainWindow.xib so save the file by pressing CMD-S.

Resources/Cell1.xib

Now it's time to create our custom UITableViewCell objects in IB, so choose "File/New" in the menu and select the "Empty" template. Select the new window that appears ("Untitled") and choose "File/Save As" from the menu. Ensure that you're in the "TableCellLoader" directory and then save the file as "Cell1". IB will ask you if you want to add the file to the project, which we do so check the checkbox and press "Add".

This file should contain the first of our UITableViewCell object, so drag a  "Table View Cell" from the "Data Views" section of the Library window (CMD-L) into the Cell1 window. The cells we're creating are UITableViewCell subclasses, so the first thing we need to do is set the class of the "Table View Cell" object by selecting it, pressing CMD-4 and select "Cell1" from the drop-down menu.

As we mentioned earlier our table view cells should contain two UILabel objects accessible through the 'label' and 'label2' properties/outlets of our Cell1 class, so let's create them. Since we want to layout the UILabel objects in a specific way we should bring up the "design window" of the "Cell1" object by double-clicking on it.

When doing this, a small table view cell shaped window should appear. Notice that the cell by default has a blue "disclosure button" to the right? We're going to use it in our tutorial so we'll keep it, but it's no problem deleting it if you want to.

Drag a "Label" object from the "Inputs & Values" section of the Library window (CMD-L) into the design window of the Cell1 object and place it to the far left in the dashed rectangle. Drag another "Label" object from the Library window and place it to the far right in the dashed rectangle (see screenshot).

In order to be able to access these labels from our applications we need to connect them to the 'label' and 'label2' outlets we created in the Cell1.h file. To do this, CTRL-drag from the "Cell1" object in the Cell1 window to the leftmost label object in the "Cell1" design window and select the 'label' outlet in the window that pops up. Repeat the process to connect the rightmost label to the 'label2' outlet.

As we have mentioned in earlier tutorials, the table view tries to reuse its cells as a way to optimise its performance. The rationale behind this is that if object creation is kept to a minimun the performance will increase. In order for this reuse scheme to work, each "type" of cell in the table needs to be assigned a "reuse identifier". If there are two types of cells in a table there are only two different identifiers, even if the total amount of cells (rows) is much larger. The UITableViewCell property which specifies the "reuse identifier" is called 'reuseIdentifier' but here in IB it's just called "Identifier", which you can see if you select the "Cell1" object and press CMD-1. Enter "Cell1" in the Identifier field.

Now it's time to configure the "File's Owner" object. Start by changing the class to "CellOwner" since we previosly explained that "CellOwner" will be the owner of all our IB created cells. Do this by selecting "File's Onwer", press CMD-4 and choose "CellOwner" from the drop-down menu.

Once the class is set to "CellOwner" we can connect the 'cell' outlet of the "File's Owner" object to the "Cell1" object. Do this by CTRL-dragging from "File's owner" to "Cell1" and choose the 'cell' outlet from the window that pops up.

We're done with this file now, so save it by pressing CMD-S.

Resources/Cell2.xib

Cell2.xib is almost identical to Cell1.xib so repeat all the steps from above but lay out the UILabels a bit differently so it's possible to discern between the two cells. I chose to place the first label slightly to the left of the center of the dashed area and the second label slightly to the right instead of to the left and right extremes (see screenshot).

When you're done with all the connections, class changes, etc. remember to save the file by pressing CMD-S. 

A nice way of seeing if all the files in IB are saved is to activate the "Window" in IB and look at the list of window names at the end of the menu. Unsaved windows have a small dot the left of the name, so if you've save all windows you should see no dots.

Classes/TableCellLoaderAppDelegate.m

Now it's time to return to XCode to implement the required methods of the UITableViewDelegate and UITableViewDataSource protocols since we connected the 'delegate' and 'dataSource' outlets of our "Table View" object in IB to the "Table Cell Loader App Delegate" object which is implemented in TableCellLoaderAppDelegate.m.

We're going to work with the Cell1 and Cell2 classes so start by importing the corresponding h-files by adding the following right after the already existing #import statement:

#import "Cell1.h"
#import "Cell2.h"

After that we should configure the number of rows in our table by adding the following just below the already present 'dealloc' method:

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return 20;
}

Finally, we're coming to the really interesting part of this tutorial - how to provide our custom made, IB designed UITableViewCell objects to the table view. We do this adding a quite impressive 'cellForRowAtIndexPath' method just below the 'numberOfRowsInSection' method.

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
// init the return value to nil
UITableViewCell *cell = nil;
if ( (indexPath.row & 1) == 0 ) {
// "even rows", that is, row 0, row 2, row 4, etc.
// check if the table view has a cell of the appropriate type we can reuse
Cell1 *cell1 = (Cell1 *)[tableView dequeueReusableCellWithIdentifier:@"Cell1"];
if ( cell1 != nil ) {
// yes it had a cell we could reuse
NSLog(@"reusing cell '%@' (%p) for row %d...", cell1.reuseIdentifier, cell1, indexPath.row);
} else {
// no cell to reuse, we have to create a new instance by loading it from the IB file
NSString *nibName = @"Cell1";
[cellOwner loadMyNibFile:nibName];
// get a pointer to the loaded cell from the cellOwner and cast it to the appropriate type
cell1 = (Cell1 *)cellOwner.cell;
NSLog(@"Loading cell from nib %@", nibName);
}
// set the labels to the appropriate text for this row
cell1.label.text = [NSString stringWithFormat:@"this is..."];
cell1.label2.text = [NSString stringWithFormat:@"...row %d", indexPath.row];
cell = cell1;
} else {
// "odd rows", that is, row 1, row 3, row 5, etc.
// check if the table view has a cell of the appropriate type we can reuse
Cell2 *cell2 = (Cell2 *)[tableView dequeueReusableCellWithIdentifier:@"Cell2"];
if ( cell2 != nil ) {
// yes it had a cell we could reuse
NSLog(@"reusing cell '%@' (%p) for row %d...", cell2.reuseIdentifier, cell2, indexPath.row);
} else {
// no cell to reuse, we have to create a new instance by loading it from the IB file
NSString *nibName = @"Cell2";
[cellOwner loadMyNibFile:nibName];
// get a pointer to the loaded cell from the cellOwner and cast it to the appropriate type
cell2 = (Cell2 *)cellOwner.cell;
NSLog(@"Loading cell from nib %@", nibName);
}
// set the labels to the appropriate text for this row
cell2.label.text = [NSString stringWithFormat:@"this is..."];
cell2.label2.text = [NSString stringWithFormat:@"...row %d", indexPath.row];
cell = cell2;
}

// return the cell which will be either a "Cell1" or "Cell2" object.
return cell;
}

I have tried to explain what's going on in the inlined comments so I won't bore you with repeating all that here in the text. Instead I think we're more than ready to see some results - yeah, it's time for a test run!

Test run

Build and run the project in XCode by pressing CMD-Return and once the simulator have started you should see a table where the "even rows" have one appearance and the "odd rows" another. Try to scroll down until you get to row 19 where the table ends. If you switch back to XCode and bring the console window to the front (CMD-R) you should see something like this.

2009-05-29 09:56:37.479 TableCellLoader[14364:20b] Loading cell from nib Cell1 for row 10...
2009-05-29 09:56:37.488 TableCellLoader[14364:20b] Loading cell from nib Cell2 for row 9...
2009-05-29 09:56:37.491 TableCellLoader[14364:20b] Loading cell from nib Cell1 for row 8...
2009-05-29 09:56:37.493 TableCellLoader[14364:20b] Loading cell from nib Cell2 for row 7...
2009-05-29 09:56:37.497 TableCellLoader[14364:20b] Loading cell from nib Cell1 for row 6...
2009-05-29 09:56:37.501 TableCellLoader[14364:20b] Loading cell from nib Cell2 for row 5...
2009-05-29 09:56:37.508 TableCellLoader[14364:20b] Loading cell from nib Cell1 for row 4...
2009-05-29 09:56:37.513 TableCellLoader[14364:20b] Loading cell from nib Cell2 for row 3...
2009-05-29 09:56:37.522 TableCellLoader[14364:20b] Loading cell from nib Cell1 for row 2...
2009-05-29 09:56:37.525 TableCellLoader[14364:20b] Loading cell from nib Cell2 for row 1...
2009-05-29 09:56:37.527 TableCellLoader[14364:20b] Loading cell from nib Cell1 for row 0...
2009-05-29 09:56:39.434 TableCellLoader[14364:20b] Loading cell from nib Cell2 for row 11...
2009-05-29 09:56:39.514 TableCellLoader[14364:20b] reusing cell 'Cell1' (0x52d780) for row 12...
2009-05-29 09:56:39.610 TableCellLoader[14364:20b] reusing cell 'Cell2' (0x52d200) for row 13...
2009-05-29 09:56:40.001 TableCellLoader[14364:20b] reusing cell 'Cell1' (0x52c870) for row 14...
2009-05-29 09:56:40.082 TableCellLoader[14364:20b] reusing cell 'Cell2' (0x52c360) for row 15...
2009-05-29 09:56:40.154 TableCellLoader[14364:20b] reusing cell 'Cell1' (0x52c0e0) for row 16...
2009-05-29 09:56:40.482 TableCellLoader[14364:20b] reusing cell 'Cell2' (0x52bb60) for row 17...
2009-05-29 09:56:40.543 TableCellLoader[14364:20b] reusing cell 'Cell1' (0x52b610) for row 18...
2009-05-29 09:56:40.576 TableCellLoader[14364:20b] Loading cell from nib Cell2 for row 19...
2009-05-29 09:56:42.298 TableCellLoader[14364:20b] reusing cell 'Cell1' (0x528fd0) for row 10...
2009-05-29 09:56:42.448 TableCellLoader[14364:20b] reusing cell 'Cell2' (0x526280) for row 9...

As we have seen in earlier tutorials, no reusal of cell is going on for the first rows since they all are visible and every visible row needs its own UITableViewCell instance. Once we start scrolling, the reuse scheme kicks into effect though. It's not just the first rows that have their own instances though. As you can see row 19 was also loaded/created from the IB file (nib).

Adding interaction

If you want to interact with the cells (detecting row selection or "disclosure button" touches) you can add the following to TableCellLoaderAppDelegate.m:

- (void)tableView:(UITableView *)tableView accessoryButtonTappedForRowWithIndexPath:(NSIndexPath *)indexPath {
NSLog(@"accessoryButtonTappedForRowWithIndexPath: row=%d", indexPath.row);
}

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
NSLog(@"didSelectRowAtIndexPath: row=%d", indexPath.row);
[tableView deselectRowAtIndexPath:indexPath animated:YES];
}

This won't do anything else but log some messages in the XCode console window, but it opens up a world of possibilities. It also demonstrates that the "disclosure button" added by default by IB works right out of the box.

Summary

There are many ways of using Interface Builder created table cells in your application but the procedure I have presented here in this tutorial is quite simple to understand, at least for a novice iPhone developer like myself . Remember that things I write about in this blogs are things that I have recently began understanding myself! That is, I am no expert and don't claim to present the best, or even correct, way of doing things. What I'm trying to say is that I welcome all kinds of comments! ;)