Archive for category Cocoa

Notes on Nib files and retaining IBOutlets

Nib files store the application’s user interface (views) in “freeze-dried” state. When you initialize a view controller with the appropriate Nib file, a copy of the outlet objects is created from the “frozen” one in the Nib file and the IBOutlet ivars in your class are hooked up to point to the newly created objects.

This unfreezing happens every time the view is loaded. Each time the view is resurrected in this manner it will appear like the original in the Nib file. So if you load a view and then make modifications (say change the layout) when the view is unloaded and reloaded, the modifications will not be visible.

From the resource programming guide:

“Nib files are the documents produced by the Interface Builder application. A nib file describes the visual elements of your application’s user interface, including windows, views, controls, and many others. It can also describe non-visual elements, such as the objects in your application that manage your windows and views. Most importantly, a nib file describes these objects exactly as they were configured in Interface Builder. At runtime, these descriptions are used to recreate the objects and their configuration inside your application. When you load a nib file at runtime, you get an exact replica of the objects that were in your Interface Builder document. The nib-loading code instantiates the objects, configures them, and reestablishes any inter-object connections that you created in Interface Builder.”

Contents of a Nib file:

Interface objects: These are the user interface elements in the application – views, windows, buttons, tables etc. Interface objects can also represent non-visual objects like controller objects that your application uses to manage the visual objects.

File’s Owner: This is a proxy object and represents the object that you will create in code and pass to the the Nib loading code. If your Nib file and the class of your object have the same name then simply calling alloc and init for your object will automatically load the correct Nib file. For example, consider the following files:

MyTableViewController.m

MyTableViewController.h

MyTableViewController.xib

Here, I assume MyTableViewController.{h,m} is a class derived from UITableViewController (or NSTableViewController). When an object of this type is created by calling


MyTableViewController *mtvc = [[MyTableViewController alloc] init];

Automatically, the object is associated with the nib file named MyTableViewController.xib and the corresponding elements from the nib file are loaded and the outlets in the object are hooked up.

This behavior can be changed in the following way – open the nib file in interface builder. Select “File’s Owner” object and in the identity inspector, set the class to any class in your code.

First Responder: This is also a proxy object and represents the first object in the application’s dynamic responder chain. For more information about the responder chain and how it is used to dispatch events in AppKit–based applications, see Event Architecture in Cocoa Event-Handling Guide. For information about the responder chains and handling actions in iPhone applications, see Event Handling Guide for iOS.

Note: Only a very simple application should store all its user interface components in a single nib file. It is better to distribute components across multiple nib files. Creating smaller nib files allows you to load only required portions of the interface.

Memory management and Nib file objects:

Each time a nib file is loaded the underlying code creates a NEW copy of the objects in the file. This means that your code is responsible for releasing the objects when it is done. How this needs to be done varies depending on the platform and memory model:

Mac OS X managed memory model: Top level objects in the nib file have a positive retain count. Objects that have parents are retained by their respective parents and do not have additional retain counts. Your code is responsible only for releasing the top level objects.

Mac OS X garbage collected memory model: Objects in the graph are kept in memory through strong memory references. But top level objects do not have strong references. Your code is responsible to create the strong reference to top level objects to prevent the object graph from being released.

iOS managed memory model: Objects are retained either using a setter method (setValue:forKey:0 or by default (if no setter is available). Objects in the graph that are not at the top level are retained by their parents (created autoreleased so they will have retain counts equal to the number of parents). Any objects that are hooked up to your class using IBOutlets will have additional retain counts. Your code is responsible for releasing all top level objects as well as all outlets.

Important note:
There is a difference in Mac OS X and iOS managed memory models. In the former, all objects are retained and they are connected via the object graph to the top level objects. So sending release to the top level objects will “propagate” the release to the other objects in the graph. In iOS, in addition to top level objects all IBOutlets have to also be released.

Good programming conventions:
Always have outlets for your top level objects and define setter methods to retain and release them. Remember to release all your outlets in -dealloc as well as -viewDidUnload. Doing this will make your code portable across platforms. Example:

@interface MyController: UIViewController {
        IBOutlet UITextField *textField;
}

@property(retain) IBOutlet UITextField *textField;
@end

@implementation MyController
@synthesize textField;

-(void) viewDidUnload {
        self.textField = nil;
}

-(void) dealloc {
        self.textField = nil;
        [super dealloc];
}
@end

This will ensure that IBOutlets are always retained in both iOS and Mac OS X (managed memory model) platforms. Your code will have to release the outlets in -viewDidUnload (iOS only) as well as -dealloc. Note that setting the textField ivar to nil will release it (the setter method will release the ivar before setting the new value) and also set it to nil.

The above implementation leaves IBOutlets exposed to “tampering”. So someone else using your code could in theory access your outlets and mess with them (say displaying something in the text field). You can get some protection against that using the implementation below where the @property is moved to the implementation file.

MyController.h

@interface MyController: UIViewController {
        IBOutlet UITextField *textField;
}

@end

MyController.m

@interface MyController()
@property(retain) IBOutlet UITextField *textField;
@end

@implementation MyController
@synthesize textField;

-(void) viewDidUnload {
        self.textField = nil;
}

-(void) dealloc {
        self.textField = nil;
        [super dealloc];
}

@end

Since code distributions (library, frameworks etc) usually include only a compiled binary along with header files the @property will be hidden from other users of your code. But remember that there is nothing “private” about methods in objective-c. If a MyController object (with class defined like above) gets a -setTextField: or -textField message it will respond and do the right thing. The only “private” part is that without access to MyController.m one would not know the setter exists!!

Another (popularly called out) nit in the above code is about calling an instance method from -dealloc (i.e. the setter). Usually it is not a good practice to call instance methods from -dealloc because within this method the state of your object is undefined. There are no guarantees that the instance method can behave correctly. So the usual preferred way to implement -dealloc is

-(void)dealloc {
        [textField release];
        [super dealloc];
}

While it is usually good practice not to call instance methods from -dealloc in this instance I believe it is safe unless someone inherits from MyController and decides to override -setTextField: to do something funky!! But sometimes it is tempting to avoid this convention for the following convenience (put in big scary note in .h file warning against overriding setters):

MyController.m

@interface MyController()
@property(retain) IBOutlet UITextField *textField;
@end

@implementation MyController
@synthesize textField;

-(void) releaseOutlets {
self.textField = nil;
}

-(void) viewDidUnload {
        [self releaseOutlets];
}

-(void) dealloc {
[self releaseOutlets];
        [super dealloc];
}

@end

In iOS, each time you add an IBOutlet you have to make sure you remember to release it in two methods. Easier to do the above and save some trouble.

Reference:

Cocoa Bindings Documents

  1. Cocoa Bindings Programming Topics: Introduction to Cocoa Bindings Programming Topics
  2. Cocoa Bindings Reference: Introduction to Cocoa Bindings Reference
  3. Key-Value Coding Programming Guide: Introduction
  4. Key-Value Observing Programming Guide: Introduction to Key-Value Observing Programming Guide
  5. Value Transformer Programming Guide: Introduction to Value Transformers

Objective-C Notes

Fast enumeration:

     NSString *element
     NSArray *myArray;
     for (element in myArray) {
     }

     for (NSString *foo in myArray) {
     /*do stuff*/
     }

Cocoa Animation in Mac OS X Tiger

Link to Apple Docs: Cocoa Drawing Guide, Animation Programming Guide

Summary of Cocoa Initializer Options

The correct way to write initializers in Cocoa.

Objective-C and Cocoa Notes

Quicknotes on Objective-C and Cocoa:

self – refers to this object (similar to this in C++)
super – refers to super class object (no c++ equivalent)

Later you can do

if([someObj respondsToSelector:mySelector]) {
    [someObj performSelector:mySelector withObject:self];
}

Selectors are used for Target/Action in Cocoa.

Introspection:
Determine the class of an object at runtime.

if([myClass isKindOfClass:[NSControl class]]) {
    /*true if myClass is an object of type NSControl or of any class that
     * inherits from NSControl*/

}

if([myClass isMemberOfClass:[NSControl class]]) {
    /*true if myClass is an object of type NSControl*/
}

Identity vs Equality:

if (obj1 == obj2) {
    /*same object instance*/
}

if ([obj1 isEqual:obj2]) {
    /*logically equivalent*/
}

Protocols (Formal, informal):
Adding interfaces for others to implement
To declare an informal protocol -

@interface BaseObject (MyProtocol) : NSObject
method1();
method2();
@end

Any classes that implement this protocol have to declare the methods again in their own interface files and define them along with other methods in their implementation files.

Formal protocols –
Advantages over informal protocols – can check at runtime if an objects implements a protocol.

@protocol ProtocolName
//method declarations
@end

Implementing class:
@interface ClassName: MySuperClass <protocol1, protocol2>
@end

The implementing class must import the header  where the protocol is declared. In objective-C 2.0 classes implementing a protocol are required to implement all of the methods.

Check if an object conforms to a protocol:
if ([receiver conformsToProtocol:@protocol(MyProtocol)]) {
}

Categories: Adding methods to existing classes
Example:

#import “ClassName.h”

@interface ClassName (CategoryName)
//new method names
@end

@implementstion ClassName (CategotyName)
//method definitions
@end

Foundation classes:

NSEnumerator – used to iterate over collections (NSArray, NSSet, NSDictionary
etc). A little bit like STL iterator

NSEnumerator *en;
id object;

en = [aCollection objectEnumerator];

while((object = [e nextObject]) != nil) {
}

Misc useful functions:
All these functions return a NSString*

NSUserName() – logon name of current user
NSFullUserName() – full nam of current user
NSHomeDirectory – Home dir of current user
NSHomeDirectoryForUser(NSString *userName) – home dir of specified user
NSSearchPathForDirectoriesInDomains(…) – useful function, read the docs :-)

Memory management:

Objective-C uses reference counting. Every object you create holds a counter within itself that counts the number of pointers pointing to the object. This count can be increased by sending the object a retain message and decreased by sending it a release message. When an object finally receives a release message which decreases it’s count to 0 then it is deleted.

You are responsible for releasing objects that you create using any of the following class methods (or their variants) -

alloc, copy, new

These methods return objects with count set to 1 (so you have to [object release] when you are done with the object). If you want to hold on to data returned from other methods you have to send that object a “retain” message. Later when you are done with that object you sent it a release method.

When an object is added to an array, dictionary, set etc the collection retains it. It will be sent a corresponding release message when the collection itself is released.

Obj-C 2.0 (from Leopard onwards) introduces garbage collection. Not available on Tiger

Links:
Hold Me, Use Me, Free Me
Introduction to memory management programming guide for Cocoa;

Cocoa iCal Framework for Tiger

There is an iCal API available for Leopard. For Tiger there is no API but you can try using the Private framework (CALCore.framework). These frameworks are supposedly not to be used (hence private). Apple provides no documentation and updates can break your code.

Useful links:

Cocoa: Archiving objects, delegating the “Application”

How do you delegate the application? In Xcode 2.5 the Interface Builder has
no object named NSApplication in the Instances tab. Apparently the icon “File’s
Owner” is the same as your application. Delegate that object to your controller
and implement any of the NSApplication delegate methods.

Archiving:

Your object must implement the NSCoding protocol to be archivable. All you have
to do is something like this:

file.h


#import < Cocoa/Cocoa.h >

#import < Foundation/NSCoder.h >

@interface FTModel : NSObject < NSCoding >

{

@public

int myInt;

NSString *myWord;

}

-(void) encodeWithCoder:(NSCoder *) coder;

-(id) initWithCoder:(NSCoder *) coder;

@end

file.m


#import "file.h"

@implementation FTModel

-(void) encodeWithCoder:(NSCoder *) coder

{

[coder encodeInt:myInt forKey:@"favNum"];

[coder encodeObject:myWord forKey:@"favWord"];

}

-(id) initWithCoder:(NSCoder *) coder

{

if(self=[super init]) {

myNum = [coder decodeIntForKey:@"favNum"] ;

//You have to retain this obj or you are in trouble

myWord = [[coder decodeObjectForKey:@"favWord"] retain];

}

return self;

}

@end

And in the place where you are going to archive the FTModel object:


FTModel *ftModel;

.

.

.

//archive object to file

[NSKeyedArchiver archiveRootObject:ftModel toFile:fileName];

//read the object

ftModel= [[NSKeyedUnarchiver unarchiveObjectWithFile:fileName] retain];

Once you implement the NSCode protocol the above calls to NSKeyedArchiver just
work.