WWDC 2011—Initial Impressions


Today Apple began the annual WWDC week with a keynote that introduced OS X Lion, iOS 5, and iCloud. If you haven’t done so yet, check out the keynote stream. Rather than just re-iterate the new features, I’d like to share some of my thoughts.

Lion

At first, I have to say that as far as features go, it was a bit underwhelming. I do have to say , I’ve been using pre-release seeds of Lion. Because of the NDA, I can’t talk too much more about the exact features…

Having seen and used these features, I realized that from a new user’s perspective the features are actually really compelling. Why did I find them underwhelming? It’s because the features have integrated themselves so smoothly and seamlessly into my daily experience that they’re really not obtrusive. Some commentary:

  • Gestures: I love them. Takes a bit of getting used to at first, but really integrate into my user experience. I love being able to touch everything and directly interact with it. My only complaint is that there is (at the moment, at least) no built-in ability to define custom gestures.
  • Full Screen: Very useful on laptops, not so much on desktops. Could be useful in some cases, but I’m not sure I like it at all. Its usefulness is doubtful, in my opinion.
  • LaunchPad & MAS: The App Store is actually really nice. Convenient, easy, accessible—everything is as advertised. As far as LaunchPad goes, I find it easy enough to use, and with the gesture to invoke from my Magic Trackpad, it joins my repertoire of app launching, along with the Applications stack, Spotlight, and LaunchBar. Not always as smooth as advertised though.
  • AutoSave, Versions, etc.: Very helpful. ‘Nuff Said.
  • Resume: Usually very useful, for when I have a bunch of documents open in Preview, but can be annoying at times. Sometimes the point of quitting an app is to clear out all the windows. It’s a bit more annoying when the files take a while to load, as they might when launching QuickTime. But very useful after a reboot.
  • Mail: Great new interface, no loss of functionality—great recipe.
What happens is that OS X is less of a new OS, and more of a new paradigm. With the further integration of iCloud, it definitely fits this role. That’s also part of the reason why it’s $29.99—a great price for a preview of the future of computing. And yes, I honestly believe that.

iOS 5

  • Notifications: This one was a long time coming. Predicted by rumors for months, this is probably the best way to do notifications in a mobile platform. Very well done.
  • Twitter: I don’t use Twitter. I don’t get Twitter. …
Logo of WebKit framework

WebKit

  • Safari: The tabs are frankly a bit ugly, but the rest of the stuff is awesome. I’ve been a proponent of WebKit since the original iPhone. I love Safari Reader on the desktop, and I’m glad they brought it to iOS.
  • Reminders: Everyone needs this. The problem with To-Do lists though is forgetting to actually use them. Not sure what Reminders is going to do for that.
  • Camera: How do I up the volume in camera mode…? Actually, the features are really nice. If the iPhone 5 bumps the camera specs a bit, it could become a serious competitor to higher-end point-and-shoots, or maybe even some dSLRs…?
  • The New Keyboard: The keyboard on the original iPad always seemed just a little bit too small. I like being able to split the keyboard, but can’t help thinking that simply typing with my thumbs might not be the most efficient. Of course, this isn’t an issue that can be fixed overnight. Why couldn’t the screen have been just a tiny bit bigger…?

iCloud

Actually not sure what to think about this yet. The features look compelling enough, but without having half a dozen Apple devices not sure how well this’ll work. And plus, some companies, schools, etc. might block anything besides in-house cloud services, so its practicality might be limited. We’ll see.

First Impressions

So, that’s some initial commentary. I kept them to short blurbs for a reason—they’re first impressions. Incidentally, for developers, first impressions are absolutely vital. So, what are your thoughts? Post them in the comments below.

What’s in it for us?

Well, all the new APIs must mean something. What happens, though, is that Lion is literally bringing the iOS design paradigms to the desktop. Which means that user interfaces can be re-thought. Instead of simply relying on individual mouse clicks, gestures are now a now interface interaction method. This allows for much more fluid interfaces. What happens is that the design of user interfaces now takes prominence. Always emphasized with iOS, this design-based nature will trickle down to OS X. These interactions are visible in the new Mail, Safari, and iCal (^^). Sometime there will no longer be a single pair of coordinates for a mouse pointer—there will only be multi-touch and gestures. That is the future. And that is what developers must work for.

Extension 14: Structs and Objects


Objective-C is an extension to regular C. So of course, at some point there is a place where all the new stuff in Objective-C ties into regular C. This Extension covers this happy meeting place. Some of the concepts may be a bit foreign, but they will be further explained in later Lessons.

Objects are Represented as Structs in Memory

You may have noticed that accessing a member of a struct uses dot notation, the same way you could access instance variables in your objects. This is because the ivars are actually stored inside a struct, which holds your instance variables plus any inherited ones. The alloc method reserves enough memory space to hold one of these structs.

Speaking of inherited instance variables, every class inherits a variable called isa (is-a) which identifies the object’s class—that is how the runtime system figures out the identity of the object, even if it’s assigned to a generic id type.

id is a Generic Pointer Type

The id type can be used for any object type. The id type is a generic pointer, which means that it is simply a memory address. The system at compile time does not know about the type of the object (that is what the isa variable is for), but simply that there is a location of memory involved. Because of this, and the fact that all objects reside in memory, the id pointer can be reassigned to different object type variables as needed.

An Object is Always Used as a Pointer

The asterisk associated with every object type is a pointer—a variable that actually holds as its value a memory address. A declaration such as

Fraction *myFract;

means that you’re defining a pointer, which will point to something in memory of type Fraction.

The reason everything is passed as pointers is one of performance. A memory address is a much smaller amount of storage than the actual object. On a 32-bit system, a memory address takes up 32 bits—usually the same size as an int. On a 64-bit system, an address would take up 64 bits. While it may seem a bit redundant at first to store memory addresses in memory, it starts to make more sense when you realize that you could have an object that holds several kilobytes of data in one object—it’s a lot faster an easier to copy 64 bits than several thousand of them.

The alloc method returns an object pointer of type id. You can then assign this pointer value to any other pointer value—in this case, perhaps a type Fraction.

When you assign one pointer to another, as in

fract1 = fract2;

You’re still assigning the value of fract2 to fract1—but remember that the value of fract2 is a memory address. So you’re making fract1 refer to the same location in memory as fract2. Therefore, anything you do to fract2 will be reflected in fract1—because of course they now refer to the same actual object in memory.

The Dual-Nature of Objects

An Objective-C object consists of instance variables and methods. The ivars are stored as a struct, and referred to using more efficient pointers. Methods are not stored in memory. Instead, methods (which are conceptually different from functions—the former is specific to a class, while the latter is global) are placed in a pool of methods, associated with the class name, and “selected” as needed.

Objective-C Lesson 14: Structs and Unions


Structs

Structs are a C construct that allows for grouping of items into one variable. For example, if you wanted to store a date, you could use three separate variables for the day, month, and year. This approach would work if you had to store one or two dates. But if you’re writing a calendar application, for example, having to keep track of which month belonged with which year would be a nightmare. Fortunately, C structs allow you to group these values into one type. The syntax for a date struct might look like this:

struct date {int month; int day; int year;};

You can then define a variable of this type as struct date today, yesterday;. However, we can use our friend the typedef statement to make this code a bit neater:

typedef struct {int month; int day; int year;} Date;
Date today, yesterday;

To access values within a struct, you use the dot operator. To set the month, you might write today.month = 5;. To get a value, the same procedure applies: int year = today.year;.

In action, it might look like this:

#import 

int main (int argc, char *argv[]) {
	NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
	typedef struct {int month; int day; int year;} Date;
	Date today;
	today.month = 5;
	today.day = 22;
	today.year = 2011;
	
	NSLog(@"Today's date is %i/%i/%i", today.month, today.day, today.year);
	[pool drain];
	return 0;
}

Initializing Structures

The fastest way to initialize a structure is to enclose the values in a pair of braces:

Date today = {5, 22, 2011};

You can list fewer values, and they will be filled in until the end of the values you list. The remaining values will be undefined.

Date today = {5};

This sets the month to 5, but leaves the remaining fields with garbage values from RAM.

You can also initialize the values out of order:

Date today = {.day = 22, .month = 5; .year = 2011};

Structs Within Structs

C allows you to put structs with structs. For example, Apple provides the CGRect data type, which defines a data type by being a struct containing a CGPoint and CGSize:

struct CGRect {
   CGPoint origin;
   CGSize size;
};
typedef struct CGRect CGRect;

You can then access the values by chaining the dot operator. To get the width, you would write

float width = rect.size.width;

Unions

Unions are a data construct that allow you to store more than one type of data in the same storage area. They are typically used in more advanced constructs. If you needed a variable, x, that could hold either an int, float, or char type, you could define a union as such:

union mixed {int i; float f; char c;};

Unions are syntactically similar to structs, but inside there are not three pieces of memory, only one. You can used the mixed type to hold either an int, or float, or char, but not two at the same time. Setting a new value overrides the previous, and you must ensure that you pull out the same type as you put in; otherwise you’ll get strange results. Accessing the field is the same as with a struct:

// x is such a union type
x.c = 'E';
x.f = 39.28;
x.i = 7/2;

You can initialize the union with a single value in braces, and it will be assigned to the first field:

union mixed x = {'$'};
// You can also specify a field
union mixed x = {.f = 3.14};

Structs and unions are more advanced C data types that are nonetheless used in lower-level frameworks, such as Apple’s Quartz framework for drawing stuff to the screen. It is important to realize that they are not objects, though. They are merely data types, with no methods to access.

Extension 14: Advanced KVC


Last week’s Lesson covered the basics of KVC, and it is those features that would be used 90% of the time. But KVC has some more to offer. Let’s take a look.

Batch Processing

Another one of KVC’s methods is dictionaryWithValuesForKeys:. The single argument is an array of strings. From that, the invokes valueForKey: with each key, and returns a dictionary with the keys and values. For example, to get some attributes of a piece of merchandise from our hypothetical store, we could use the following code:

Merchandise *product = [[store valueForKeyPath:@"merchandise"] lastObject];	// Last object in array
NSArray *keys = [NSArray arrayWithObjects: @"brand", @"price", @"department", @"shelf", @"netWeight", nil];
NSDictionary *productInfo = [product dictionaryWithValuesForKeys:keys];

You could then use this dictionary in an inventory tracking program, for example.

This method also has a counterpart, setValuesForKeysWithDictionary:. Called on an instance of any object, this method goes through each key in the dictionary, and replaces that key’s value in the object with the new value.

nil Values

Here is one minor issue that you might run into with KVC. You can’t put nil into a dictionary (or array for that matter), because nil tells the collection that it’s the end of data. Therefore, you have to use [NSNull null] instead, and that’s the value you’ll get back if you call valueForKey: on an object type. But what happens if you try to set nil for a scalar type, such as an integer? By default, you get an error at runtime. Therefore, you have to override setNilValueForKey: and define your own implementation. In our store, the method might look like this:

- (void)setNilValueForKey:(NSString *)key {
	if ([key isEqualToString:@"price"])
		[self setValue:[NSNumber numberWithInt:0] forKey:@"price"];
	else
		[super setNilValueForKey:key];
}

Error Handling

What happens if you try to access or set a value for a key that doesn’t exist? In general, you get the compiler or runtime warning that looks like “this class is not key value coding-compliant for the key someKeyThatDoesn’tExist.” Fortunately, when KVC encounters this it will ask the class what to do. As such, there are two methods that you can override: valueForUndefinedKey: and setValue:forUndefinedKey:.

A simple way to handle this issue is to simply tell the user that the key is invalid. So to protect our store from crashes, we’re going to just do that:

- (void)setValue:(id)value forUndefinedKey:(NSString *)key {
	NSLog(@"Cannot set anything for this key. The key %@ is not valid.", key);
}

- (void)valueForUndefinedKey:(NSString *)key {
	NSLog(@"Cannot get value for this key. The key %@ is not valid.", key);
}

Obviously, KVC is a very powerful feature, and allows you to build very robust, abstract applications. In fact, it is the core data access mechanism in Core Data, Apple’s framework for accessing data from a database. But that’s a complex topic for another day.

Objective-C Lesson 13: Key-Value Coding


Accessing ivars is usually done through accessor methods or dot-notation. But as a means of indirection, there is another way—Key-Value Coding, or KVC. This is a means of setting and getting the values by using strings—specifically, NSString, and its corresponding methods, including the very useful stringWithFormat:.

Methods

KVC involves another layer of abstraction, in the form of two specific methods: -valueForKey: and -setValue:forKey:. These methods work as they do for NSDictionary (which, unsurprisingly, is built upon the same principles). The key is an NSString, constructed as a constant or with any one of NSString’s methods. The code is simple:

NSString *name = [myObject valueForKey:@“name”];

The key is in fact the same name as your variable. It follows a specific sequence of steps:

  1. First, it looks for a getter method with the same name. In the above example, it looks for the -name or -isName (for boolean values) methods; if they are found, it invokes that method and returns that result.
  2. If the getter methods are not found, the method looks for an instance variable by that name: name or _name.

This is a very important mechanism: -valueForKey: uses the metadata in the runtime stack to look into your custom objects. This ability is not available in other languages. Speaking of other languages, experienced Java programmers are familiar with the concepts of autoboxing—converting int and other scalar types into corresponding object types, such as Integer and Double. KVC is the only Objective-C construct that supports autoboxing—it automatically converts scalar types to NSNumbers or NSValues. It will also do the opposite if you set one of these types to a scalar value. Note, however, you must still manually “box” a scalar value before using it in the setValue:ForKey: method:

[self setValue:[NSNumber numberWithFloat:249.42] forKey:@“price”];

If price is a float, the value will be automatically unboxed.

Key Paths

You can combine KVC and dot syntax to create a key path. Imagine that we have a store class; we can use the following syntax:

[store setValue:[NSNumber numberWithFloat:2.99] forKeyPath:@“gallonOfMilk.price];

That is the same as

store.gallonOfMilk.price = 2.99;

These key paths can be as deep as you want them to be.

Aggregated Results

NSArray *prices = [store valueForKeyPath:@“merchandise.price”];
// Merchandise is an array

If you ask an NSArray for a value for a key, it will create another array, ask each object for the value specified in the key path, and adds these values to the new array. It basically loops over each value in the array, and sends each object the method valueForKeyPath:@“price”.

Advanced Operators

  • @count:
    count = [store valueForKeyPath:@“merchandise.@count”];

    The @count directive tells the KVC mechanism to get the count of the result of the rest of the key path.

  • @sum:
    totalPrice = [store valueForKeyPath:@“merchandise.@sum.price”];

    The @sum directive gets the sum of all the requested values—in this case, the sum of all the prices of the merchandise.

  • @avg:
    avgPrice = [store valueForKeyPath:@“merchandise.@avg.price];

    The @avg directive gets the average of all the values.

  • @min and @max:
    minPrice = [store valueForKeyPath:@“merchandise.@min.price”];
    maxPrice = [store valueForKeyPath:@“merchandise.@max.price];

    These two are self-explanatory: they return the maximum or minimum of all the values in the range.

  • @distinctUnionOfObjects:
    NSArray *brands = [store valueForKeyPath:@“merchandise.@distinctUnionOfObjects.brand”];

    This key returns a list of individual distinct values—here, it returns a list of all the brands in our hypothetical store.

Note that, unfortunately, you cannot add your own operators.

One More Application

Imagine that you were creating a lottery application (or more likely, a lottery view within another application). You might have 10 views, each with a number, and you highlight each image as a number is drawn. In this case, you’d probably have ten instance variables, maybe named numberView1, numberView2, all the way to numberView10. To light up the views, you could have a large if…else or switch block, comparing the new value to all the values 1–10:

int nextNum = arc4random() % 10 } 1;	// Random number generator, 1–10
    switch (nextNum) {
        case 1:
            // Highlight
            break;
        // etc.
    }

Knowing KVC though, there is a more elegant solution:

[[self valueForKey:[NSString stringWithFormat:@“numberView%d”, nextNum]] highlight];	// Assume highlight method exists and works

Update: That method can be found in this sample project: Click here to download.

Objective-C Lesson 12: Exception Handling


Because of Objective-C’s dynamic runtime there are cases where errors go unnoticed until runtime. These might include sending messages an object doesn’t respond to, or going out of bounds of an array. Sometimes you may not even catch these errors while you’re testing—for example, say you had an application that allowed users to access a value in an array by entering an index value. If the user entered ’20′ when there are only 5 values in the array, the index would be out of bounds; in your testing, you may never have thought to check such numbers.

Granted, the above example is rather contrived, but you should still try to anticipate such issues and do something about them, so your program doesn’t crash. Resolving these issues generally includes logging them and informing the user—an incorrect value, in the above example, or if it’s an error the user can’t fix (sending the wrong message), notify the user and then gracefully exit.

Runtime issues such as these are typically exceptions. If uncaught, your program will quit. Let’s look at an example.

#import <Foundation/Foundation.h>
int main(int argc, char *argv[]) {
	NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
	NSArray *array = [[NSArray alloc] initWithObject:@"A string"];
	[array nonExistantMethod];
	NSLog(@"Object at index 5 is %@", [array objectAtIndex:5]);
	[array release];
	NSLog(@"No issues!");
	[pool drain];
	return 0;
}

Building the program nets you a warning that your array may not respond to nonExistantMethod (obviously). If we run it, we get a whole bunch of info:

2011-05-02 19:18:59.492 Exceptions[760:707] -[__NSArrayI nonExistantMethod]: unrecognized selector sent to instance 0x100113e90
2011-05-02 19:18:59.525 Exceptions[760:707] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[__NSArrayI nonExistantMethod]: unrecognized selector sent to instance 0x100113e90'
*** First throw call stack:
(
	0   CoreFoundation                      0x00007fff8f509406 __exceptionPreprocess + 198
	1   libobjc.A.dylib                     0x00007fff941a09ea objc_exception_throw + 43
	2   CoreFoundation                      0x00007fff8f584ade -[NSObject doesNotRecognizeSelector:] + 190
	3   CoreFoundation                      0x00007fff8f4db8a3 ___forwarding___ + 371
	4   CoreFoundation                      0x00007fff8f4d8178 _CF_forwarding_prep_0 + 232
	5   Exceptions                          0x0000000100000e33 main + 195
	6   Exceptions                          0x0000000100000d64 start + 52
	7   ???                                 0x0000000000000003 0x0 + 3
)
terminate called throwing an exception

Wading through that, we see some indications that array does indeed not respond to nonExistantMethod. For education’s sake, we’re going to keep that line, and instead use the @try…@catch() blocks. This is what our program now looks like:

#import 

int main(int argc, char *argv[]) {
	NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
	NSArray *array = [[NSArray alloc] initWithObject:@"A string"];
    @try {
        [array nonExistantMethod];
    }
    @catch (NSException *exception) {
        NSLog(@"Caught exception %@", exception);
    }
	NSLog(@"Object at index 5 is %@", [array objectAtIndex:5]);
	[array release];
	NSLog(@"No issues!");
	[pool drain];
	return 0;
}

The code in the @try block is your usual program code. The code in the @catch block is your error handling code—here, we just log the exception. In fact, the next NSLog will also cause an exception—an out of bounds exception. We can therefore move that line into the @try block.

int main(int argc, char *argv[]) {
	NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
	NSArray *array = [[NSArray alloc] initWithObject:@"A string"];
    @try {
        [array nonExistantMethod];
        NSLog(@"Object at index 5 is %@", [array objectAtIndex:5]);
    }
    @catch (NSException *exception) {
        NSLog(@"Caught exception %@", exception);
    }
	[array release];
	NSLog(@"No issues!");
	[pool drain];
	return 0;
}

The results are now

2011-05-02 19:33:09.308 Exceptions[872:707] -[__NSArrayI nonExistantMethod]: unrecognized selector sent to instance 0x100113e90
2011-05-02 19:33:09.312 Exceptions[872:707] Caught exception -[__NSArrayI nonExistantMethod]: unrecognized selector sent to instance 0x100113e90
2011-05-02 19:33:09.313 Exceptions[872:707] No issues!

Note now that our program no longer crashes, but instead catches the exceptions, and continues. Note that the @try…@catch block is exited after the first exception is caught, regardless of if there are any others.

You can also append a @finally { } block that executes regardless of whether there has been an exception or not.

Throwing Exceptions

Unlike in Java, Objective-C exceptions should not be thrown whenever there is an error—try to handle the error, or use something like NSError or the NSAssert() method.

As Apple states, “You should reserve the use of exceptions for programming or unexpected runtime errors such as out-of-bounds collection access, attempts to mutate immutable objects, sending an invalid message, and losing the connection to the window server. You usually take care of these sorts of errors with exceptions when an application is being created rather than at runtime.
If you have an existing body of code (such as third-party library) that uses exceptions to handle error conditions, you may use the code as-is in your Cocoa application. But you should ensure that any expected runtime exceptions do not escape from these subsystems and end up in the caller’s code. For example, a parsing library might use exceptions internally to indicate problems and enable a quick exit from a parsing state that could be deeply recursive; however, you should take care to catch such exceptions at the top level of the library and translate them into an appropriate return code or state.”

Throwing exceptions is a rather “expensive” procedure, and of course uncaught exceptions will cause your program to crash. That being said, throwing an exception is straightforward:

NSException* myException = [NSException
        exceptionWithName:@"FileNotFoundException"
        reason:@"File Not Found on System"
        userInfo:nil];
@throw myException;
// [myException raise]; /* equivalent to above directive */

A more popular usage is in the case where you can’t recover from the issue (invalid data, for example)—you could catch the exception to perform some cleanup, then throw it back to the system for something else to catch, or to quit at that point.

Exceptions provide a powerful and flexible way to handle issues in your code. Use them wisely—don’t make the runtime system a massive juggling round.

Extension 13: Complex Initializers


The subject of initializer methods deserves a bit of special attention. Most importantly, in addition to the default initializer method that is inherited from NSObject, most classes also have other initializers that take certain arguments. All initializers, however, follow the same pattern. Let’s look at an example.

Convenience Initializers

Additional initializers are often known as convenience initializers because you can initialize the object and set some or all of its instance variables (and perhaps other values) in one method call. To begin, let’s take a look at some of NSString‘s initializers. These include initWithString:, which takes an NSString as an argument and creates a new string from that, initWithFormat:, which takes a formatted string and a corresponding number of arguments, and plugs in the proper values for each format specifier, and returns the completed string, and initWithContentsOfFile:encoding:error:, which creates a string out of any text file. The latter example is a perfect example of the convenience nature of these methods—doing the same with C library functions would be a lot more difficult. Note also that NSString has class methods that do the same thing (except that according to the memory management rules, the values are autoreleased).

If we were to create a Rectangle class, you might have an initializer like this:

-(id)initWithWidth:(float)w andHeight:(float)h {
    if(!(self = [super init]))
        return nil;
    self.width = w;
    self.height = h;
    return self;
}

Not too much that’s new there. After the initialization, we assign proper values to our instance variables and return. Let’s add another initializer:

-(id)initWithWidth:(float)w andArea:(float)a {
    if(!(self = [super init]))
        return nil;
    self.width = w;
	self.height = (a / w);
	return self;
}

Not much that’s new here. We’ve simply created another convenience initializer.

The Designated Initializer

The interesting bit comes when we decide to subclass our Rectangle class. Suppose we create a ColoredRectangle, or (and this is perhaps a bad example) ColoredCube. We would be adding new instance variables—at least a color ivar, and maybe a further depth measurement. Normally, the subclass would have to override all of those methods, and if the superclass added another initializer, that would mean that all subclasses would have to override that as well.

Fortunately, Cocoa has a solution—the designated initializer. Typically the one with the most arguments, all the parallel initializers call that one, and the subclasses all use the designated initializers in their calls to super. Choose an initializer to be the designated initializer, and follow through with it:

// Rectangle's initializers
-(id)initWithWidth:(float)w andHeight:(float)h {
    if(!(self = [super init]))
        return nil;
    self.width = w;
    self.height = h;
    return self;
}

-(id)initWithWidth:(float)w andArea:(float)a {
    if(!(self = [self initWithWidth:w andHeight:(a / w)]))
        return nil;
	return self;
}

// ColoredCube's initializers
-(id)initWithWidth:(float)w andHeight:(float)h {
    if(!(self = [super initWithWidth:w andHeight:h]))
        return nil;
    self.depth = 1.0;    // Default value
    self.color = [UIColor redColor];    // Ditto
    return self;
}

-(id)initWithWidth:(float)w height:(float)h depth:(float)d andColor:(UIColor *)c {
    if(!(self = [super initWithWidth:w andHeight:h]))
        return nil;
    self.depth = d;
    self.color = c;
    return self;
}

Note here that we only have to override the default initializer in the subclass, because all the other inherited initializers will then call on that overridden initializer. We can also add more initializers as necessary.

By creating a default initializer (note that there is not specific syntax to denote the designated initializer—it’s up to the programmer to determine which one is the designated and stick with it), it makes subclassing a lot easier.

Objective-C Lesson 11: Object Initialization


By this point we’ve covered the basics of the Objective-C language itself. With this knowledge and a willingness to learn, everything else can be picked up from Apple’s developer portal and the documentation. However, there are still topics which are used consistently through the language and are certainly worth talking about. This week’s topic is the creation of objects in your code—proper practices and syntax.

Allocation

Creating a new object in Objective-C is usually a two-step process. First, memory has to be allocated for the object, then the object is initialized with proper values.

The first step is accomplished through the alloc class method, inherited from NSObject and rarely, if ever, overridden. This method literally allocates a chunk of memory sufficient to hold your object (all of its instance variables). It then sets all of these values to zero or nil—this is so that you won’t get garbage values left behind by whatever had last used that chunk of memory. This means that all objects start out as nil, BOOLs start as NO, ints as 0 and floats are set to 0.0. These values are guaranteed after the alloc method is called.

Fraction *frac = [Fraction alloc];
// numerator and denominator are both 0

However, that object is not quite ready to use yet—it has to be initialized first (otherwise strange bugs are mostly guaranteed). This is accomplished by the init instance method, which is often overridden. These methods return an object of type id—the same object that is being initialized.

Fraction *frac = [[Fraction alloc] init];

It is important that you nest the alloc and init methods—there are instances where the initialization method may not return the same object as was allocated. This is true when working with classes such as NSString, which are in fact a public interface for a variety of classes. In these cases, the initialization method may choose to return an object that is not quite the originally requested class (for example, NSString‘s initialization methods might return a subclass of NSCFString.)

An initialization method for our Fraction class might look like this:

- (id)init {
	if (!(self = [super init]))	// Location 1
		return nil;		// Location 2
	// Initialize fraction to 3/5	// Location 3
	numerator = 3;
	denominator = 5;
}

Let’s look at this code. At Location 1, we have…well, a source of controversy, actually. In general, the recommended practice is to do it as shown, for a detailed breakdown of the issue, check out this link. Basically, this line states that if there is an issue with super’s init method and it returns nil, then your init method should also stop and return nil. Note that when a method hits a return statement, the method ends, even if there’s code left. This is why an else… block is not necessary. Why might [super init] not work? One case is if the object was being initialized with data from the internet. If there isn’t an internet connection, or if the internet resource can’t be found, [super init] might return nil, rather than some made-up or garbage data. At Location 2, we return nil, if necessary.

The method then assigns initial values to the instance variables at Location 3. In general, they should be set to reasonable, “empty” values if possible. Many programmers would (redundantly) set these values to zero, just to make their intentions clear. Here is a design decision you have to make. Assume you have a large class that has a lot of instance variables—a Car, for example. In the init method, you can choose to set default values for everything, if the intended use is to create a basic Car and use it. However, if the user is expected to extensively customize the Car, you might want to simply leave containers for the values, and not set default values.

Convenience Initializer

Most classes also have convenience initializers, which take arguments with which to set the ivars.

-(id)initWithNumerator:(NSInteger)num overDenominator:(NSInteger)denom {
	if (!(self = [super init]))
		return nil;
	numerator = num;
	denominator = denom;
}

The code is quite self-explanatory. The method assigns the values in the arguments to the class’s instance variables.

In the case of multiple initializers, it is advised that one of them be the designated initializer—generally it is the most complex initializer. This topic will be covered in next week’s Extension.

Constructors

Languages such as Java have a concept of constructors, which are language-level constructs that are automatically invoked through the new keyword. They combine the process of allocation and initialization, although most of the custom code is for the latter. They also have syntax restrictions—the method must not return anything (that’- how it becomes a constructor), it must have the same name as the class, any call to super must be the first statement… Objective-C has none of these restrictions. As stated above, initializers are not part of the language itself—there is no keyword to invoke them. As such, they are simply regular methods, which usually end up calling NSObject’s init method. In Java, calling the superclass’s implementation must be the first line, to ensure that you’re not dealing with garbage data. There is no such restriction in Objective-C—but any issues with garbage data are the responsibility of the programmer, not the compiler. This is one of C’s design goals—give responsibility to the programmer. But with great power comes great responsibility!

Objective-C Lesson 10: Memory Management


Memory on the iPhone is limited. Although each new generation of hardware has increased the amount of physical memory in the device, applications are also taking more memory, and with the introduction of background apps in iOS 4, memory remains a resource to be conserved. iOS automatically quits applications that use too much memory; therefore it’s important to be aware of your memory usage and avoid leaking, which will increase your memory usage, or the equally annoying early release of elements, which will cause your app to crash. These issues are handled by the system in a garbage-collected environment, where the system will automatically release memory as needed, but this adds an overhead and is not available on iOS. On a similar topic, note that iOS does not have a paging system—data cannot be paged out to disk as needed. You can read and write data to disk, but this is not recommended (or even possible) for any object—only for data files, and even then sparingly, because disk access is inevitably slower than memory access.

Reference Counting

Cocoa’s memory management scheme is based off a reference counting system. It’s a simple concept—every object has a value, known as a reference count. When the object is created, the count is set to 1. Certain actions will increment or decrement this count, and when the count hits zero, the object is immediately freed from memory, at which point you cannot access it again.

Messages

Reference counting principally involves three methods—retain, release, and dealloc. The first two change the reference count; the latter frees up the memory of the objects. You generally override the dealloc method, as we have been doing in our classes, to free up the memory occupied by the class’s instance variables (usually by calling their dealloc method). Finally, the method always includes a call to super‘s dealloc method, which invariably calls NSObject‘s dealloc method. It is this specific method that frees up the memory; if you neglect to call this implementation, you get a compiler warning. Also note that you should always override this method, but you should never call it specifically—the system will call that method for you as necessary.

The retain method increments the retain count by one. It is inherited by all classes, and so can be called on any object, any number of times, at any time. The release method decrements the retain count by one.

Keeping track of the retain count requires some diligence on the part of the programmer. If you under-release an object, the memory will be leaked; if you over-release, you crash.

Memory Management Rules

The rules are simple, really.

  • If the method name has init, copy, or new anywhere in the method name (such as initWithName:, mutableCopy, or newCar), the method implies that the object will have been retained, and it is your responsibility to release that object later This also means that you’ll need to store that object in a variable, to be able to release it again.
    NSMutableArray *ma = [someArray mutableCopy];
    myMutableArray = ma;
    [ma release];
  • Any other method name, if the method returns an object, will be autoreleased.

All of Apple’s methods follow these rules, and yours should too.

Memory Management with Properties

When you create a property, you can specify certain parameters that the setters follow. Of note is the retain and copy parameters. Most object properties are declared as retain, which generates a setter like so:

-(void) setText:(NSString *)textValue {
    if (![textValue isEqualToString: text]) {
        [textValue retain];
        [text release];
        text = textValue;
    }
}

This value is retained, and as such you have to balance it out with a release in the dealloc method. The copy parameter does something similar, except that instead of retaining the new value, it copys it.

One thing to point out, as noted by a comment on last week’s post, is that in general you do not want to retain delegate objects. Otherwise, you’ll get a recursive memory coupling. For example, a table view’s delegate is generally the view controller which owns the table view itself. Therefore, if the delegate property retained the delegate, the table view would own its delegate, which would own the table view—you couldn’t release any one of them because they own each other. Make sure to avoid this issue.

Autorelease

The autorelease pool is a medium between directly managing memory, and garbage collection. A lot of methods return autoreleased objects—these objects are added to an autorelease pool, which is created in every program. When the drain method is sent to the pool, all of the objects within are sent a release method. This can be useful in a tight even troop:

NSAutoreleasePool *tempPool;
for (i = 0; i < n; i++) {
     tempPool = [[NSAutoreleasePool alloc] init];
     // Create lots of temporary autoreleased objects that take a lot of memory
     [tempPool drain];
}

To autorelease an object, simply send the autorelease method.

Collection Classes

Collection classes, including NSArray, NSDictionary, and NSSet, retain all of the objects that you put into them. When the collection class is deallocated, all of the members in the class are also released.

On any platform, specifically the memory-constrained iOS platform, memory management is an important topic to keep in mind. It requires diligence on the part of the programmer—but the alternative is slow performance and/or crashes, both of which must be avoided. Following the simple rules is the fool-proof way to keep track of your memory management.

Objective-C Lesson 9: Protocols


A protocol is a list of method declarations that is not bound to any one class. The methods are not implemented in the protocol; instead, classes conform to or adopt a protocol and within those classes the methods are implemented. A protocol is simply a grouping of certain related methods under one name, and it acts like a contract—conforming classes must implement the required methods (and may implement any of the potential optional methods).

The syntax for declaring a protocol is simple:

@protocol NSCopying
- (id)copyWithZone:(NSZone *)zone;
@end

The text in bold is required syntax. The name of the protocol is written in italics. Between the mandatory lines is the method(s) of the protocol, declared as you would normally declare methods. The protocol is typically saved in a .h (header) file, and follow the same naming conventions as classes do.

For a class to adopt one or more protocols, place the protocol names between angle brackets, separated by commas if there is more than one. A class that adopts the

NSCopying

protocol might be declared like this:

@interface CopyMachine : NSObject <NSCopying, NSCoding>

The compiler sees that the CopyMachine class conforms to the NSCopying and NSCoding protocols, and it will issue a warning if the required methods in those classes are not implemented. Note also that protocol conformity is inherited—subclasses of CopyMachine, for example, will also conform to those protocols, and can re-implement (override) those methods as necessary.

Let’s look at the theory behind protocols.

Why Protocols?

Apple’s documentation on protocols states that there are at least three reasons to use protocols:

  • To declare methods for others to implement
  • To declare the public interface to a class while hiding the nature of the class itself
  • To mark similarities between classes that are not part of the same inheritance tree

In fact, there is a fourth reason as well, and in everyday use it is the most compelling: It lets you avoid the complexities of subclassing, similar to the way categories do, but in a different manner.

Protocols play an important role in OS X and iOS development—a lot of built-in classes use them. A classic example is the UITableViewController, which is a view controller that manages a table view (basically a scrollable list, like in the iPod application). Without protocols, users of the class would have to subclass the original class, deal with any issues, and override the original methods. This is another challenge—for the original class. For something like a table view, where the data for the view may come from a variety of locations and there may be a myriad of behaviors to handle, what is an acceptable default implementation? Using protocols circumvents the issue. UIKit also includes two protocols, designed to work with UITableViewControllers. They are UITableViewDataSource and UITableViewDelegate. Any class that adopts these protocols will be able to control a table view.

Apple’s documentation on protocols effectively describes the other reasons. Let’s return to the syntax.

Other Protocol Functionality

Optional Methods

Methods in a protocol can be declared to be optional, by using the @optional directive. This works like the ones for instance variable scope—it acts like a switch. The opposite is @required.

@protocol Drawing
- (void)paint
- (void)erase
@optional
-(void)outline
@end

Introspection

You can check to see if a class implements a protocol by calling the conformsToProtocol: method. The single argument is of type Protocol, declared with the @protocol (protocolName) directive.

if ([someObject conformsToProtocol:@protocol(NSCopying)])   …

Applications with id type

The id type is designed to be for any object type, but you can use the compiler to ensure that only objects that conform to a protocol are assigned to the object. Note that this only works with a statically type object being assigned to the id type, as it is a compiler check. This type of usage is often found when classes have an ivar or property for their delegate, which is an id type that conforms to a corresponding delegate protocol. The syntax looks like this:

id <NSCopying> delegate;

Remember that this is a compiler check, so it only works with statically typed variables and objects.

Java Interfaces

Java programmers may recognize the similarity between protocols and Java’s interfaces. In fact, the former was the inspiration for the latter. The main benefit of interfaces in Java is so that a collection can hold a number of objects of different classes, which could all be upcast to a shared interface—an object could implement many interfaces. Because NSArray can already hold objects of different types without limitations, this is not the intention of protocols in Objective-C. Rather, protocols are used extensively in Cocoa as a communications medium. This best explained by Apple’s documentation on delegation:

“A delegate is an object that acts on behalf of, or in coordination with, another object when that object encounters an event in a program. The delegating object is often a responder object—that is, an object inheriting from NSResponder in AppKit or UIResponder in UIKit—that is responding to a user event. The delegate is an object that is delegated control of the user interface for that event, or is at least asked to interpret the event in an application-specific manner.

To better appreciate the value of delegation, it helps to consider an off-the-shelf Cocoa object such as a text field (an instance of NSTextField or UITextField) or a table view (an instance of NSTableView or UITableView ). These objects are designed to fulfill a specific role in a generic fashion; a window object in the AppKit framework, for example, responds to mouse manipulations of its controls and handles such things as closing, resizing, and moving the physical window. This restricted and generic behavior necessarily limits what the object can know about how an event affects (or will affect) something elsewhere in the application, especially when the affected behavior is specific to your application. Delegation provides a way for your custom object to communicate application-specific behavior to the off-the-shelf object.

The programming mechanism of delegation gives objects a chance to coordinate their appearance and state with changes occurring elsewhere in a program, changes usually brought about by user actions. More importantly, delegation makes it possible for one object to alter the behavior of another object without the need to inherit from it. The delegate is almost always one of your custom objects, and by definition it incorporates application-specific logic that the generic and delegating object cannot possibly know itself.”

  • Welcome

    Xcode App Icon (Glow) (Icon)

    My goal is to make CupsOfCocoa into a beautiful source for beginners to the iPhone platform to get started. Subscribe below for more, and stay tuned!

  • Contact Me

    If you need to contact me for any reason, feel free to send me an email.
  • The Giving Spirit

    If you've found this site helpful, would you consider donating a little sum? Any amount is appreciated...Thanks so much!

  • StackOverflow

  • Roadmap

  • Enter your email address to follow this blog and receive notifications of new posts by email.

    Join 88 other followers

  • Back to the Past

    May 2012
    S M T W T F S
    « Apr    
     12345
    6789101112
    13141516171819
    20212223242526
    2728293031  
  • Time Machine

  • You count!

    • 174,053 views
  • Worldwide Stats

    free counters
Follow

Get every new post delivered to your Inbox.

Join 88 other followers