Handy Little Tool for Errors


During the development of any application, there will be times when you have to track errors in your code. Having the errors be clearly expressed in the Console or in an alert will help with the debugging. As an example, rather than letting your application crash with an exception, it might make sense to log a clear error and defer the fix to a later time.

Because of this, I’ve made a simple little macro that formats the error in a nate way. This is the macro:

#define LOCATION_AS_NSSTRING [NSString stringWithFormat:@"Function: %s\nLine: %d\nFile: %s", __func__, __LINE__, __FILE__]
#define STRING_REPRESENTATION_OF_ERROR(x) [NSString stringWithFormat:@"\n==== ERROR ====\nError %d: %@\nSeverity: %@\n%@", x, x ## _STRING, x ## _SEVERITY, LOCATION_AS_NSSTRING]

It is actually two macros; the former is a “helper” macro and you interact with the latter. The macro works with three other preprocessor declarations. It’ll make the most sense to put this in context:

/******* Severity Levels ******/
#define SEVERITY_NO_ACTION @"No action required"
#define SEVERITY_LOW @"Low"	// Minimal user action to fix OR cannot fix, but doesn't severly affect usability
#define SEVERITY_HIGH @"High"	// Extensive user action to fix OR cannot fix, and affects usability somewhat
#define SEVERITY_CRITICAL @"Critical"	// Crashers; cannot recover from issue
#define SEVERITY_UNKNOWN @"Unknown"	// Not enough information to decide

#define ERROR_CODE_1 1	// No sections defined for library
#define ERROR_CODE_1_STRING @"No sections defined for library browser. Returning 1"
#define ERROR_CODE_1_SEVERITY SEVERITY_NO_ACTION

// … Some code later …
NSLog(@"%@", STRING_REPRESENTATION_OF_ERROR(ERROR_CODE_1));

This code example will output the following:

==== ERROR ====
Error 1: No sections defined for library browser. Returning 1
Severity: No action required
Function: <ContainingMethod>
Line: <SomeLineNumberOfCalling>
File: <ContainingFile>

So how does this work? It’s actually rather simple. You pass in the error code listing, and the preprocessor “assigns” this value to the “variable” x. Then it uses that variable in the standard NSString method call. It first “plugs in” the value of x, then combines the full token (remember that you pass in the entire token, ERROR_CODE_1, so that gets combined and forms ERROR_CODE_1 ## _STRING, which gets joined into ERROR_CODE_1_STRING). It then does the same thing with _SEVERITY token, resulting in ERROR_CODE_1_SEVERITY. Finally, it plugs in the results from the other macro, LOCATION_AS_NSSTRING, which in turn calls three built-in macros (__func__, __LINE__, and __FILE__), which return the calling function, line, and file is C-style strings.

So really, it’s quite simple. There are just a lot of macros involved, and it really becomes quite useful. As an example, you could use this to show a meaningful error to users with an alert, if necessary.

Floating in Uncertainty


Among the primitive data types that Objective-C offers are float and double. Similar to long, double is simply double the storage in bits of float. However, as the name suggests, both are floating-point numbers. That is, the decimal point literally “floats” around as necessary. This can lead to many subtle bugs, even between executions of the same program.

How Do They Work?

Both types have a certain numbers of bits for storage. The exact number of bits varies per implementation, just as the exact storage sizes for ints and doubles aren’t fixed. However, a floating point number effectively has two parts, the “whole” part and the “fractional” part. The floating nature comes in because the number of bits used to store each section is not always the same, and will vary depending on the sizes of the numbers involved.

The Precise Issue

Although there are an infinite number of integers, they all differ by the same amount—1. Therefore it is relatively easy to represent an integer value as bits. By contrast, however, the difference between one decimal number and another is incredibly small, and, what’s worse, this difference can change. When trying to squeeze an infinite number of digits with infinite precision into a finite number of bits, something has to give. This usually means that the floating-point value is rounded, and is not completely accurate. Here’s a simple program (using plain C) to illustrate this issue:

int main (int argc, const char *argv[]) {
	float f = 1234.123456789;
	printf("%f\n", f);
	printf("%.9f\n", f);
	return 0;
}

Output:

1234.123413
1234.123413086

The first print statement (printf() is nearly identical to NSLog(), except that it takes C-style strings instead) shows the actual value of f in memory, with ten digits in this case. Notice that the value is different from the value we assigned to the variable. This is because there are not enough bits to hold every single digit, so some of it had to be rounded away. That rounding is not perfectly accurate—it rounds the tailing ‘456789’ into ‘413’. If, however, we try to force it to display nine digits as seen in the next line, we still end up with the ‘413’, along with a tailing ‘086’—not digits that we put in.

Comparisons

How many times should this loop run?

#import <Foundation/Foundation.h>
int main (int argc, const char * argv[]){
	NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
	int count = 0;
	for (float i = 10; i != 0; i -= 0.1) {
		count++;
		printf("count is %d\n", count);
	}
	[pool drain];
    return 0;
}

Common sense tells us that it should run 100 times, but in fact this is an infinite loop. Because of these rounding errors, i will never be exactly equal to zero. It’ll come close after 100 cycles through, but it won’t be perfect. As such, when dealing with floating point numbers, you want to check to see if it’s close to a value. In the above example, the comparison should be i >= 0; (the equal bit because the floating point number might be a tiny negative value) to ensure that the loop ends.

To check if two floating point numbers are equal, you could use the == (double equals) operator, but there’s no guarantee that they will give an accurate result, especially after many calculations. A better way (albeit less efficient) would be to get the absolute value of their difference, and see if it’s less than a small number, such as 0.00001. If it’s less, you can safely assume they’re equal; otherwise, they’re not.

Floating point inaccuracies can be very difficult to debug, so it pays off to take precautions earlier. As a matter of fact, I spent about half an hour trying to figure out why something wasn’t showing up on screen, when in fact a botched conversion from integers to floating point (similar issues result) lead to everything being 0. Not fun.

Extension 15: Foreach Loop


Enumerating through the contents of a collection class is a very common activity. Collection classes in the Foundation framework include NSSet and NSArray. Enumerating is simply the act of going through the collection and getting every value inside—you might do this, for example, if you were doing the same thing to each one, like disabling the buttons in an NSArray of UIButtons. Normally, this would involve a for() loop, like this:

NSArray *array = // Some array
for (int i = 0; i < [array count]; i++)
    [(UIButton *)[array objectAtIndex:i] setEnabled:NO];

This kind of syntax is so common that there is a shorthand way of doing it (known officially in Objective-C as fast enumeration:

NSArray *array = // Some array
for (UIButton *button in array)
    button.enabled = NO;

The key is what goes inside the for() loop. You declare a variable of any type (to avoid a runtime crash, declare to be the type of variable in the array, such as the UIButton shown here, or if is more than one type, you could use id). Then you put the keyword in, followed by the name of the collection. Basically at this point, the new variable you created will be assigned to the “next” value in the collection at every iteration of the loop. In the body of the loop, you can do whatever you want to the variable. Note though that if you need to access the index of the object, you’ll have to go back to the original style.

An Update


So, it’s been a few weeks since I’ve have time to post here. Unfortunately this busy-ness will last for a few more days, so there won’t be any new content until then. But more importantly, the content on this site—at this point, the Objective-C portion is mostly completed. Over the next few weeks, then, I’ll be wrapping up the Objective-C portion, and move into more fun stuff. I’ve noticed a surprising lack of Xcode 4 information; seeing how it has been released for several months now, lots of people must be using it. So that’ll be really useful, and I’m itching to dive right in.

Thanks to everyone for stopping by, and I hope this site has been helpful. Over the next few weeks, I hope to add much more content, and make this little slice of the Internet even more useful. 🙂

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 nonExistentMethod];
	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 nonExistentMethod (obviously). If we run it, we get a whole bunch of info:

2011-05-02 19:18:59.492 Exceptions[760:707] -[__NSArrayI nonExistentMethod]: 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 nonExistentMethod]: 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 nonExistentMethod. 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 nonExistentMethod];
    }
    @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 nonExistentMethod];
        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 nonExistentMethod]: unrecognized selector sent to instance 0x100113e90
2011-05-02 19:33:09.312 Exceptions[872:707] Caught exception -[__NSArrayI nonExistentMethod]: 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:@"IndexOutOfBoundsException"
        reason:@"Attempted to access an array index that is out of bounds"
        userInfo:nil];
@throw myException;
// [myException raise]; /* equivalent to throwing the exception, above */

A more popular usage is in the case where you can’t recover from the issue (invalid data, for example, or where you tried to access an index that is beyond the size of an array)—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.

  • Welcome

    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!

  • Roadmap

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

    Join 220 other followers

  • Back to the Past

    May 2020
    S M T W T F S
     12
    3456789
    10111213141516
    17181920212223
    24252627282930
    31  
  • Time Machine

  • You count!

    • 622,650 views
  • Worldwide Stats

    free counters
%d bloggers like this: