Understanding Cocoa
Relevant resources
Documentation
Web
Books
Design patterns
Model View Controller
Cocoa is designed around this pattern. The code for your application is split into three silos:
This pattern helps to decouple your code, making it easier to design and maintain. Your code also becomes more reusable, because your views and model are completely independent of each other. This is why you only need to subclass the provided iPhone user interface elements if you want to do completely custom items. Your model can even be truly cross-platform using C++, running on Mac, iPhone, Android, etc.
Cocoa tries to push you in this direction with the structure of the frameworks, so don't try to fight this.
For more, see "The Model-View-Controller Design Pattern" in the Cocoa Fundamentals Guide.
Delegates
Delegation is a pattern used in Cocoa to help prevent subclassing by providing application-specific information to generic classes, usually user interface elements. A custom class is assigned as a delegate to one of these generic classes, and if the delegate implements certain methods, those methods are called in order to provide data back to the generic class. The generic class can then be customized to meet the needs of a particular application, without having to be subclassed.
Delegation can also allow lazy-loading of data, something particularly useful on the iPhone when minimizing startup time and memory usage. Delegates are only queried at the moment that their custom data is required, such as when a UITableViewDataSource is asked for the content of a given row.
Examples of this include data sources for UITableViews, and the delegate that handles callbacks from NSURLConnections, among many others.
Category
Another way of extending classes without subclassing is through the use of categories. Categories let you add methods to existing classes, but not instance variables. To create a category, you can write interface code similar to the following:
@interface NSString (MyStringExtensions)
// Your new method definitions here
@end
and then implement those methods using code like the following:
@implementation NSString (MyStringExtensions)
// Your method implementations go here
@end
All you need to do is import the header for your new category in your code and it will be as if this class had been implemented from the beginning with these methods.
A subtle variant of this is a class extension, where instead of specifying a name for a category, you leave the parentheses empty. Class extensions are often used to add private methods to aid encapsulation (see discussion of this below). One difference that sets class extensions apart from standard categories is that you are required to implement the methods detailed in the interface for the class extension or the compiler will throw a warning at you.
When using categories, do not attempt to override methods of classes, particularly ones provided by Apple. This can lead to unexpected, and often very bad, behavior.
Notifications
Notifications can help to decouple portions of your application. A notification is an application-wide message that is posted by one instance to the central notification center, a shared instance of NSNotificationCenter called defaultCenter. Other objects across the application can listen for notifications posted to this center.
To register an instance as an observer for a particular notification, you use code like the following:
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleNotification:) name:@"MyNotification" object:nil];
This will register for a notification with the name "MyNotification". When such a notification is posted to the center, the -handleNotification: method of this instance will be triggered. You can give this notification-handling method any name, as long as it follows this format:
- (void)handleNotification:(NSNotification *)note;
{
}
These notifications can be filtered by the object portion of the addObserver method definition. If anything other than nil is specified, only notifications that were posted carrying that particular object will trigger the notification-handling method of this instance.
To post a notification, you can use code similar to the following:
[[NSNotificationCenter defaultCenter] postNotificationName:@"MyNotification" object:extraData];
The object portion of this method lets you associate a user-defined object with this notification. This lets you pass along additional information, or allow for filtering of the notifications, as described above. To access this object in your notification-handling method, you use the object method on NSNotification, like follows:
id notificationData = [note object];
When cleaning up after an object that is observing a notification from the center, you need to remove it as an observer. Otherwise, if the object is deallocated, a notification will be sent to the deallocated object and your application will crash. You can remove observers using code like the following:
[[NSNotificationCenter defaultCenter] removeObserver:self];
Key-Value Observing (KVO)
Key-Value Observing is very similar to notifications, in that you can have an object which observes certain events. In this case, the events are not notifications posted by another object, but changes in the instance variables of another object. Your observer directly attaches itself to the object under observation, and pays attention to changes in selected variables.
This lets your observer respond to changes in values of this secondary object without the secondary object directly informing the observer. For example, you might want to have a view controller observe the bounds of its view's subviews. If those subviews grow larger than the containing view, your controller could resize the containing view to fit them.
To add your instance as an observer of a keypath on another object, you can use code similar to the following:
[objectToObserve addObserver:self forKeyPath:@"keyPath" options:NSKeyValueObservingOptionOld context:nil];
A keypath is usually the name of the instance variable you are observing, but there are some exceptions to this (at least one of which we will discuss when we cover Core Animation).
The NSKeyValueObservingOptionOld option in this code means that the old value of this variable will be passed along in a dictionary returned to the observer.
The observer will need to implement a method with the following general structure to handle these events:
- (void)observeValueForKeyPath:(NSString*)keyPath ofObject:(id)object change:(NSDictionary*)change context:(void*)context
{
if ([keyPath isEqualToString:@"keyPath"])
{
[self handleChangeInValue];
}
else
{
[super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
}
}
This one method will handle all of the KVO changes you are observing, so you'll need to filter changes by keypath or object.
as with notifications, you will want to remove your observer when cleaning up the observer. You will also want to remove the observer when you want to get rid of the object under observation. This can be done using code like the following:
[objectToObserve removeObserver:self forKeyPath:@"keyPath"];
KVO notifications are only triggered if you change values of instance variables using standard accessor methods (covered later when talking about encapsulation). If you directly set the values of these variables, your observer will not be notified. However, if you wish to inform the observer yourself that such a change has taken place, you can wrap the change in code like the following:
[self willChangeValueForKey:@"keyPath"];
// Change the value here
[self didChangeValueForKey:@"keyPath"];
This will trigger the appropriate KVO notifications for that keypath.
Singletons
Also referred to as "shared instances", singletons are classes that you wish to have only instance of at any given time across your entire application. Examples include NSUserDefaults' standardUserDefaults and NSNotificationCenter's defaultCenter. Usage of singletons tends be discouraged (see the Stack Overflow question "What is so bad about Singletons?" for discussion about this), but they are useful in certain scenarios.
Sample code for a singleton class design can be found on the CocoaDev wiki (use implementation at bottom of page): http://www.cocoadev.com/index.pl?SingletonDesignPattern
If you are looking to provide cached instance variables which are only allocated once, application-wide (examples include a commonly used font or color), you can use code similar to the following:
+ (MyObject *) mySharedObjectThatEveryoneUses
{
static MyObject *sharedInstance;
if (!sharedInstance) {
sharedInstance = [MyObject alloc];
sharedInstance = [sharedInstance init];
}
return sharedInstance;
}
This specific implementation, along with why you would want to split the alloc and init apart in this case, is explained by Bill Bumgarner in this Stack Overflow answer.
Enumerators
Needing to iterate over all of the items in a collection is a common problem. Cocoa provides a couple of means of doing this. The first, and most common, method is to use fast enumeration. Fast enumeration of a collection takes the form:
for (NSString *currentString in arrayOfStrings)
{
// Do something
}
As its name suggests, fast enumeration is noticeably faster than manually enumerating elements in an array yourself. It also has a descriptive, clean syntax.
Before fast enumeration, NSEnumerators were used. They still have uses, but where you can you should use fast enumeration. The two can also be combined. For example, if you wanted to step through an array in reverse order, you could use code like the following:
NSEnumerator *enumerator = [array reverseObjectEnumerator];
for (NSString *element in enumerator) {
if ([element isEqualToString:@"Three"]) {
break;
}
}
Selectors
Objective-C is a dynamic language which lets you do a few things at runtime you might not be used to, if you come from a C or C++ background. You can change which methods are called at runtime through the use of selectors and invocations. Most of the time you won't need to, but it helps to know what's going on.
Selectors are identifiers for methods. You have probably seen them used when manually configuring UIControls, such as UIButton. The above code for handling notifications uses the snippet @selector(handleNotification:) to let the notification center know which method on the observing object to trigger upon a notification. @selector() returns the compiled selector that refers to a particular method. The type returned from this is SEL, which you might run into from time to time when dealing with the Cocoa frameworks.
To be even more clever, you can have the method to be used for a selector be determined at runtime using NSSelectorFromString(), which takes in an NSString and returns a selector for the method matching that string's value. Be careful with this, because it is easy to create very difficult to debug crashes using something like this.
Selectors are also useful when wanting to run a method after a certain period of time. For example, the following will cause the -displayHelp method, which takes no argument, to be performed one second from now:
[self performSelector:@selector(displayHelp) withObject:nil afterDelay:1.0];
When we cover multithreading, we'll see how you can use -performSelectorInBackground:withObject: to run a task on a background thread.
For another case where you might see selectors, you can use NSTimers to perform methods at regular intervals within your application. For example,
NSTimer *intervalTimer = [NSTimer scheduledTimerWithTimeInterval: 1.0 target: self selector: @selector(respondToTimer) userInfo: nil repeats: YES];
will create a timer which runs the -respondToTimer method once a second. There is a lot more to maintaining and invalidating a timer than this, but this is just an example.
Object-oriented programming
Objective-C, as the name indicates, is an object-oriented language. Object concepts can be difficult to grasp for those used to scripting languages or plain C.
Encapsulation (and properties)
The purpose of an object is to group data and code for a particular need together in a self-contained package. Ideally, an object acts as a "black box", with its internal functionings hidden from the world. An interface is maintained that other objects or functions can use, but that is all that they are allowed to access within the object. By hiding the inner workings of an object, you are free to change the code that makes that object work without worrying about breaking anything that communicates with the object.
This is another example of how you can decouple sections of your program from one another, making them easy to maintain.
Objective-C prevents you from directly accessing instance variables, forcing you instead to use accessors. These accessors typically look something like the following:
- (void)setName:(NSString *)newValue;
{
// Do the variable setting here
}
- (NSString *)name;
{
return name;
}
Note the naming convention. Methods to set a variable use the lowercase set prefix, then make the first letter of the variable uppercase. It is critical that you follow this capitalization scheme when writing your own accessor methods.
However, we have a much easier way of generating these accessors. Rather than write out this boilerplate code time and again for each variable that you want to have accessible within a class, modern Objective-C provides properties. In your interface, you use the syntax
@property(attributes) type variableName;
with an example being
@property(readwrite, nonatomic, copy) NSString *description;
The type and name usually match that of an instance variable in the class, but they don't need to. The attributes specify how the property should be implemented. These attributes are
After defining a property in the interface, you need to synthesize those properties in the implementation. You can synthesize a property using
@synthesize description;
This would create the getter and setter for the description property detailed above. That's all there is to it, two lines of code that can save many others.
As I mentioned above, there are cases where you might wish to have properties with different names than that class instance variables. Usually, this is to aid encapsulation by making it clear to the programmer when they are using accessors and when they are setting an instance variable directly. To do this, you can use code like the following:
@synthesize description = internalStringForDescription;
where description is the outward-facing name of the property and internalStringForDescription is the name of the instance variable to be set.
One more special case is a setting that lets you provide accessors at runtime. The following code
@dynamic description;
says that we will provide the getter and setter method for description at runtime. The most common time you will see this is when you are dealing with Core Data, which we will cover later.
For more on properties, consult the "Declared Properties" section of the Objective-C Programming Language guide.
Inheritance
Another powerful feature of object-oriented languages is the capability for objects to inherit from one another. When one object inherits from another, it gains all of the variables and methods of its parent. Inheritance gives you the ability to take a complex class, inherit from it, and add or replace code in the new class to alter the function of the original. This allows for significant code reuse and can save a tremendous amount of time.
To define a class that inherits from another, you use code similar to the following in its implementation:
@interface MyView : UIView
In this case, you are declaring a MyView class which descends from UIView.
If you do nothing else, it will behave just like a UIView. However, that's not why you'd want to subclass something. You can add new methods, override existing ones, and / or add new instance variables. Adding new methods is as simple as defining them in the interface and fleshing them out in the implementation. Likewise, to add a new instance variable, you simply define it in the interface.
To override a method from the superclass, you need to create a method with its same name in the implementation. This method will be run in place of the superclass's version when an instance of your new subclass is used. Before overriding a method, be sure to check the class reference for the superclass (if you are subclassing an Apple-provided class) to make sure that there isn't anything you are supposed to do in this overridden method.
For example, when overriding NSObject's -dealloc method, you need to make sure to run the default implementation of this, in addition to your own custom code. The superclass's implementation of a method can be run by sending a message to super, like in the following example:
- (void)dealloc
{
// Your custom code here
[super dealloc];
}
Polymorphism
While it has a funky name, polymorphism is a reasonably simple concept. It means that any subclass of a given class can be accessed using that main class's interface. For example, if you had created a subclass of your MyView class (which was a subclass of UIView) called AnotherView, both MyView and AnotherView could be handled as if they were UIViews. This means that you could do something like the following:
MyView *newMyView = [[MyView alloc] initWithFrame:myFrame];
UIView *viewToHandle = newMyView;
[viewToHandle setNeedsDisplay];
Even though you allocated a new object as a MyView, you can still assign it to a variable of type UIView, and send it messages that a UIView would understand. It would just handle these messages using whatever custom implementation you've overridden them with (or not, if that's the case). Polymorphism lets you slide in your custom subclasses wherever their ancestors are accepted.
Protocols
Objective-C lacks multiple inheritance (the ability to have more than one direct ancestor for a given class). However, it provides protocols as a way to solve some of the problems that multiple inheritance does in other languages. Protocols let you specify certain methods that a class must (or should) implement.
To define a protocol, you use code like the following:
@protocol MyProtocol
- (void)doSomethingInResponseToThisString:(NSString *)string;
@optional
- (void)anOptionalMethod;
@end
This states that any class implementing this protocol must provide an implementation of -doSomethingInResponseToThisString:, but doesn't need to implement -anOptionalMethod.
To make a class comply to a protocol, you add the protocol in angled brackets to the class's interface:
@interface MyView : UIView <MyProtocol>
and then implement all necessary methods in the class's implementation.
If you want to create a variable that must be any object complying with a protocol, you can use the following syntax:
id<MyProtocol> objectSatisfyingProtocol;
The "Objects, Classes, and Messaging" section of the Objective-C Programming Language guide has more on these object concepts.
Memory management
Cocoa uses a reference-counting memory management system (the Mac also has garbage collection, but not the iPhone yet). This can be a difficult concept to grasp for those coming from a background in a scripting language, or one with garbage collection. However, if you learn the rules, Cocoa's memory management model isn't all that complex.
Objects maintain a retain count. When newly allocated, that count is 1:
MyView *newMyView = [[MyView alloc] initWithFrame:myFrame];
(as an aside, I highly recommend watching"Designated Initializer" by James Dempsey and the Breakpoints to learn something about initializers.)
Every release message sent to an instance reduces its retain count by 1.
[MyView release];
Because MyView started with a release count of 1, it now has a count of 0. When an instance hits a retain count of 0, it is deallocated immediately. Its -dealloc method is called, clearing the object out of memory.
When handing an object off to another in a message, it is safe to assume that the object will be retained by the recipient of the message if it needs to hang around.
MyView *newMyView = [[MyView alloc] initWithFrame:myFrame];
[mainView addSubView:newMyView];
[MyView release];
In this case, we've created a new MyView instance and sent it to the mainView to be added to the view hierarchy. Because we no longer have a need for this object, we release it. The mainView retains it as part of the view hierarchy, so the MyView instance is not deallocated.
Assignment of variables often causes people problems when first coming to Cocoa. If you notice, we define new instances of a class as MyClass *classInstance, and not MyClass classInstance. This is because the variable actually stores a pointer to the object, not the object itself. If you were to write
NSString *newString = [[NSString alloc] initWithString:@"Hello"];
NSString *otherString = newString;
otherString would not contain a copy of newString, but a pointer to the exact same object. If you were to release newString, it would be as if you had also released otherString. To store a copy in otherString, you would need to use something like the following:
NSString *newString = [[NSString alloc] initWithString:@"Hello"];
NSString *otherString = [newString copy];
Note that copy returns a copy of an object which has a retain count of 1, so it is up to you to release it when done.
This is often the source of questions about when to use properties to set instance variables. If you were to directly set an instance variable using code like the following:
NSString *newString = [[NSString alloc] initWithString:@"Hello"];
instanceVariable = newString;
[newString release];
instanceVariable would point to a deallocated object, and your application would crash the first time it was used. Instead, if instanceVariable has been set up as a property with the signature
@property(readwrite, nonatomic, copy) NSString *instanceVariable;
you could then write
NSString *newString = [[NSString alloc] initWithString:@"Hello"];
self.instanceVariable = newString;
[newString release];
and be safe, because a copy of the string with a retain count of 1 would be stored in instanceVariable. Because your class would be responsible for this variable, you'd need to remember to release it in your class's -dealloc method.
A wrinkle is added to this through the presence of autorelease. Autoreleased objects are ones that are not immediately released, but they will be at the end of the run loop or when their autorelease pool is drained. They are created as return values from methods, where if these objects had simply been released they would have been deallocated before anything useful could be done with them.
Treat autoreleased objects as if they were going to disappear at any moment, retaining them if you need them to stick around.
Autoreleased objects can build up in memory if many are generated while executing a single method. This is a particular danger when dealing with tight loops. In these cases, you may wish to create your own autorelease pool so that you can dictate when these objects will be removed from memory. If you create an autorelease pool, autoreleased objects will be sent to it instead of the default application-wide pool:
for (int counter = 0; counter < 100; counter++)
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
// Build up autoreleased objects
[pool release];
}
Because you don't have precise control over when autoreleased objects will be released, I highly recommend avoiding them on the iPhone if you can. They can lead to unnecessary memory buildups, and memory is precious on the device.
Cocoa follows naming conventions to let you know the retain count of objects returned from methods. If a method starts with -init, -copy, or -new, it will return an object with a retain count of 1, and it is your responsibility to release that object when done. All others return autoreleased objects, or simply references to objects, and you will need to retain these if you want them to hang around.
One tip for managing memory is to pair your -init and -dealloc methods at the top of your class implementation file. That way, you can make sure that everything you allocate in your initialization is paired with a release message in -dealloc. Also, every time you add a property with retain or copy as an attribute, immediately add a release message in your -dealloc method for it.
For more on this, see the Memory Management Programming Guide for Cocoa. Of course, "Release Me" by James Dempsey and the Breakpoints is an excellent complement to this guide.