Advanced programming tips, tricks and hacks for Mac development in C/Objective-C and Cocoa.

Easy custom UITableView drawing

It is really easy to customize your UITableViews. I'll show you how to completely customize the appearance of UITableViews without overriding or subclassing and without the need for any tricky hackery.

Make my table pretty

The core of most iPhone applications is the UITableView. To make your iPhone application stand out, the simplest way is to make your UITableView look good.

Customizing your UITableView can be really easy. You don't need custom drawing code. You don't need subclasses of anything. Cocoa Touch provides all the drawing capability you need, all you have to do is use the right classes in the right ways and provide the layout.

The sample application

The approach I'll show you will turn the table on the left into the table on the right:

customtableview.png

Left: a default UITableView with three rows. Right: the same table view after customization.

How to fail at UITableView customizing

Coming from Mac OS X made it harder for me — UITableView needs to be customized in a very particular way and structurally, it is very different to Mac OS X's NSTableView and NSCell drawing.

The following are all really bad ways to customize a table (even though you can make it work):

  • Subclassing UITableView to customize the drawing of cells
  • Subclassing UITableViewCell to customize the drawing of cell content
  • Creating your own array of UITableViewCells and returning these instead of using dequeueReusableCellWithIdentifier:

About the second point: it is okay to customize UITableViewCell — but you shouldn't really use it for drawing. The UITableViewCell class is more of a controller class — it handles behaviors and layout, not drawing. You can customize UITableViewCell to load a specific contentView (and do the custom drawing there).

That last point (that you should always use dequeueReusableCellWithIdentifier:) is only peripherally related to drawing but it will significantly slow your drawing down if you avoid the normal cell queuing architecture.

How to succeed at UITableView customizing

There are only a few points to understand related to table drawing.

First: the UITableView does not itself draw anything except the background. To customize the background of a UITableView, all you need to do is set its backgroundColor to [UIColor clearColor] and you can draw your own background in a view behind the UITableView.

Second: The tableHeaderView (and the table footer and section headers and footers) need not be just a title. You can insert your own view, with its own subviews in the table header, giving layout and custom drawing freedom.

Third: UITableViewCell is composed of 5 different subviews. Customizing the right subview is the secret to good UITableViewCell drawing. The subviews are:

  1. backgroundView — the entire background of the row (including what looks like the UITableView's background in UITableViewStyleGrouped style tables.
  2. selectedBackgroundView — replaces the backgroundView when the row is selected.
  3. image — a customizable image (not actually a subview) at the left of the cell.
  4. accessoryView — a customizable view at the right of the cell.
  5. contentView — a customizable view between the image and the accessoryView (technically, it extends behind the image).

You can customize any of these (except image which must be a UIImage) using your own custom drawn views.

However, since the pixel size of the table never changes, it is often easiest just to use UIImageViews for each of them. Then you can take highly complex views drawn in separate programs, cut them into the 5 necessary pieces and let the automatic caching of UIImage's named image cache manage your memory for you.

There is an argument against drawing your views in code and that is that the iPhone's drawing is not nearly as fast as Mac OS X. Operations like gradients and multiple overlapped components can really tax the iPhone.

Custom drawing code is a good choice for simple and flat colour drawing. In most other cases — as in this post — I recommend you use UIImageView to draw your views in a table.

Implementation

With all custom drawing handled by UIImageView, that still leaves some work to do. You must handle all layout and configuring of views.

Configuration of the UITableView and layout of the table header

As an example of what that means, have a look at the viewDidLoad method for this post:

- (void)viewDidLoad
{
    //
    // Change the properties of the imageView and tableView (these could be set
    // in interface builder instead).
    //
    tableView.separatorStyle = UITableViewCellSeparatorStyleNone;
    tableView.rowHeight = 100;
    tableView.backgroundColor = [UIColor clearColor];
    imageView.image = [UIImage imageNamed:@"gradientBackground.png"];
    
    //
    // Create a header view. Wrap it in a container to allow us to position
    // it better.
    //
    UIView *containerView =
        [[[UIView alloc]
            initWithFrame:CGRectMake(0, 0, 300, 60)]
        autorelease];
    UILabel *headerLabel =
        [[[UILabel alloc]
            initWithFrame:CGRectMake(10, 20, 300, 40)]
        autorelease];
    headerLabel.text = NSLocalizedString(@"Header for the table", @"");
    headerLabel.textColor = [UIColor whiteColor];
    headerLabel.shadowColor = [UIColor blackColor];
    headerLabel.shadowOffset = CGSizeMake(0, 1);
    headerLabel.font = [UIFont boldSystemFontOfSize:22];
    headerLabel.backgroundColor = [UIColor clearColor];
    [containerView addSubview:headerLabel];
    self.tableView.tableHeaderView = containerView;
}

This method handles the configuration of the tableView (setting the backgroundColor, rowHeight and sets an image behind the table) but also creates its own layout for the table header.

The layout of the header here is for the table's header view. You can include a custom header for every table section by implementing the UITableViewDelegate method tableView:viewForHeaderInSection:. There are equivalent properties and methods for the table and section footers.

It is possible to handle this type of layout in Interface Builder and load the XIB files for this type of layout. Sadly though, on the iPhone, reading loading lots of views from XIB files is slow (I suspect this is due to slow reading from the Flash memory) and doesn't always allow configuration of every property.

For this reason, I normally sketch my views in Interface Builder and then manually recreate the same thing in code. That's what I've done here: picking coordinates for the headerLabel that looks balanced in the view.

Cell backgrounds

The cell background needs to incorporate the tops and bottoms of table "sections". For this reason, the backgroundView and selectedBackgroundView normally need to be set on a row-by-row basis.

In your tableView:cellForRowAtIndexPath: method where you are configuring the cell for a given row, this code will handle that behavior:

UIImage *rowBackground;
UIImage *selectionBackground;
NSInteger sectionRows = [aTableView numberOfRowsInSection:[indexPath section]];
NSInteger row = [indexPath row];
if (row == 0 && row == sectionRows - 1)
{
    rowBackground = [UIImage imageNamed:@"topAndBottomRow.png"];
    selectionBackground = [UIImage imageNamed:@"topAndBottomRowSelected.png"];
}
else if (row == 0)
{
    rowBackground = [UIImage imageNamed:@"topRow.png"];
    selectionBackground = [UIImage imageNamed:@"topRowSelected.png"];
}
else if (row == sectionRows - 1)
{
    rowBackground = [UIImage imageNamed:@"bottomRow.png"];
    selectionBackground = [UIImage imageNamed:@"bottomRowSelected.png"];
}
else
{
    rowBackground = [UIImage imageNamed:@"middleRow.png"];
    selectionBackground = [UIImage imageNamed:@"middleRowSelected.png"];
}
((UIImageView *)cell.backgroundView).image = rowBackground;
((UIImageView *)cell.selectedBackgroundView).image = selectionBackground;
Layout within the contentView

Layout of elements within the contentView need only be set on construction of the contentView (not on a row-by-row basis).

Sadly, laying out UILabels in the contentView (like the "Cell at row X." and "Some other infomation." lables in this example) is a little verbose.

The following code is run immediately after the allocation of the UITableViewCell to position the "Cell at row X." label:

const CGFloat LABEL_HEIGHT = 20;
UIImage *image = [UIImage imageNamed:@"imageA.png"];

//
// Create the label for the top row of text
//
topLabel =
    [[[UILabel alloc]
        initWithFrame:
            CGRectMake(
                image.size.width + 2.0 * cell.indentationWidth,
                0.5 * (aTableView.rowHeight - 2 * LABEL_HEIGHT),
                aTableView.bounds.size.width -
                    image.size.width - 4.0 * cell.indentationWidth
                        - indicatorImage.size.width,
                LABEL_HEIGHT)]
    autorelease];
[cell.contentView addSubview:topLabel];

//
// Configure the properties for the text that are the same on every row
//
topLabel.tag = TOP_LABEL_TAG;
topLabel.backgroundColor = [UIColor clearColor];
topLabel.textColor = [UIColor colorWithRed:0.25 green:0.0 blue:0.0 alpha:1.0];
topLabel.highlightedTextColor = [UIColor colorWithRed:1.0 green:1.0 blue:0.9 alpha:1.0];
topLabel.font = [UIFont systemFontOfSize:[UIFont labelFontSize]];

//
// Create a background image view.
//
cell.backgroundView = [[[UIImageView alloc] init] autorelease];
cell.selectedBackgroundView = [[[UIImageView alloc] init] autorelease];

In my mind, it seems like there should be a more efficient way to do this. I hold out the possibility that there is.

This code spends most of its time working out where the label should be placed. It needs to go right of the image, left of the accessoryView, middle of the row but above the "Some other information." label.

Other adornments

The accessoryView is just a UIImageView. The cell.image is set as a property. These are extremely simple additions but they make the table cells far more impactful.

Conclusion

You can download the EasyCustomTable project as a zip file (60kb).

The code includes a #define at the top that allows you to toggle the custom drawing on and off.

None of this is particularly revolutionary (it is all in the iPhone documentation) but it is still easy to miss the properties and methods that make it easy.

This does require custom images. If you've never drawn anything, now is a good time to learn inkscape (it's free and very good for the price). You could also use Adobe Illustrator but if you have that much money, pay an artist to draw it for you.

Layout of the content in code is probably the weakest part of the approach I've presented. To make it easier, you can pre-layout everything in Interface Builder and copy the layout into code. For complicated layouts, you could even try using nib2objc to convert your XIB files to code automatically (although I've never done this, I'm just mentioning nib2objc because the idea is so cool).

What does it mean when you assign [super init] to self?

One of the strangest pieces of common syntax in Objective-C is the line self = [super init];. Without any explanation, this arrangement raises a few questions. Does this line set the self value for the instance? Is self just a variable like any other? If so, why have it at all? I'll address each of these questions and show how the compiler converts uses of self and method invocations.

Converting a method invocation

The first step to understanding the self parameter is to look at how the compiler converts a standard method invocation.

When you type the following:

MyClass *myObject = [[MyClass alloc] initWithString:@"someString"];

The compiler converts this into function calls that look like this:

class myClass = objc_getClass("MyClass");
SEL allocSelector = @selector(alloc);
MyClass *myObject1 = objc_msgSend(myClass, allocSelector);

SEL initSelector = @selector(initWithString:);
MyClass *myObject2 = objc_msgSend(myObject1, initSelector, @"someString");

The compiler has slightly more efficient means of getting the class and SEL values but if you look at assembly code, you will see objc_msgSend calls for every method invocation.

So what is "self"?

Every method that you declare has two hidden parameters: self and _cmd.

The following method:

- (id)initWithString:(NSString *)aString;

is converted by the compiler to the following function call:

id initWithString(id self, SEL _cmd, NSString *aString);

The reality is that self is simply a hidden parameter on every method. Like any other parameter, it receives its value from the function invocation.

Yes, _cmd is also a hidden parameter on every method that you can access if you choose. In reality, there are few uses for the _cmd parameter except in obscure cases.

You can experiment with this by eliminating objc_msgSend and invoking the function for a method directly. Instead of calling:

[myObject someMethodWithParameter:someValue];

You can reach your method implementation directly by recreating the work done by objc_msgSend.

SEL methodSelector = @selector(someMethodWithParameter:);
IMP someMethodFunction = class_getMethodImplementation([myObject class], methodSelector);
someMethodFunction(myObject, methodSelector, someValue);

The only reason why self has a value on the inside of the someMethodWithParameter: implementation is because the pointer myObject is passed as the first parameter into someMethodFunction. If you pass a different value as this first parameter, then self will have a different value on the inside of the method.

If you pass a value of a different class, you have a good chance of crashing the program. The following section explains why.

Why have a "self" parameter at all?

A method needs to know what data to act upon. The self parameter tells the class the data to act upon and so is essential to object oriented programming.

This statement may seem a little strange, since you can easily implement a method without using the self parameter by name. The reality is that the compiler uses the self parameter to resolve any reference to an instance variable inside a method.

If you had a class defined like this:

@interface MyClass : NSObject
{
    NSInteger value;
}
- (void)setValueToZero;
@end

then the method:

- (void)setValueToZero
{
    value = 0;
}

is converted by the compiler into:

void setValueToZero(id self, SEL _cmd)
{
    self->value = 0;
}

So self is essential for accessing any instance variables, even if you never literally type "self".

So does self already have a value when init is called?

If you remember back at the start, I said that the initWithString: part of a typical [[MyClass alloc] initWithString:@"someString"] invocation is converted into an objc_msgSend call:

MyClass *myObject2 = objc_msgSend(myObject1, initSelector, @"someString");

So by the time we get to the inside of the method, self already has a value; its value is myObject1 (i.e. the allocated object, as returned from the [MyClass alloc] call. This is essential because without it, the super invocation wouldn't be possible — the self value is used by the compiler to send the invocation:

[super init];

becomes:

objc_msgSendSuper(self, @selector(init));

Yes, self already has a value when your initializer starts. In fact, it is almost guaranteed to be the correct, final value.

So why assign the value returned from [super init] to self?

Looking at a typical initializer method:

- (id)initWithString:(NSString *)aString
{
    self = [super init];
    if (self)
    {
        instanceString = [aString retain];
    }
    return self;
}

Why do we assign [super init] to self here?

The textbook reason is because [super init] is permitted to do one of three things:

  1. Return its own receiver (the self pointer doesn't change) with inherited instance values initialized.
  2. Return a different object with inherited instance values initialized.
  3. Return nil, indicating failure.

In the first case, the assignment has no effect on self and the instanceString is set on in the original object (the line instanceString = [aString retain]; could have been the first line of the method and the result would be the same).

In the third case, the initialization has failed. self is set to nil, no further action is taken and nil is returned.

The rationale for assigning to self is associated with the second case: if the returned object is different, we want the:

        instanceString = [aString retain];

which gets converted to

        self->instanceString = [aString retain];

to act on the correct value, so we have to change the value of self to point to this new object.

It's almost never required to initialize self

So the rationale for assigning to self is that the [super init] could return a different object and should initialize that different object instead of the old (likely invalid) object.

The question to consider is then: when would [super init] return a different object?

The answer is that it will return different objects in one of the following situations:

  • Singleton object (always returns the singleton instead of any subsequent allocation)
  • Other unique objects ([NSNumber numberWithInteger:0] always returns the global "zero" object)
  • Class clusters substitute private subclasses when you initialize an instance of the superclass.
  • Classes which choose to reallocate the same (or compatible) class based on parameters passed into the initializer.

In all but the final case, continuing to initialize the returned object if it changes is a mistake — the returned object is already completely initialized and isn't necessary related to your class anymore.

So the list of three things that [super init] is permitted to return can now be expanded to four by splitting the "Return a different object" point into two:

  1. Return its own receiver (the self pointer doesn't change) with inherited instance values initialized.
  2. Return an object of the same class, requiring further initialization.
  3. Return a different object that is already completely initialized.
  4. Return nil, indicating failure.

In this list, we now have two cases (2 and 3) which are incompatible. The typical "assign [super init] to self" initializer handles cases 1, 2 and 4.

An init approach to handle cases 1, 3 and 4 would actually be:

- (id)initWithString:(NSString *)aString
{
    id result = [super init];
    if (self == result)
    {
        instanceString = [aString retain];
    }
    return result;
}

So class clusters, singletons and unique objects all use case 3, putting dozens of Cocoa classes in this category. I'm only aware of NSManagedObject that uses case 2. Curiously then, while case 3 is overwhelmingly more common, initializers that support 1, 2 and 4 but are incompatible with case 3 have become the standard.

Conclusion

Update: I have rewritten this conclusion to reflect the fact that I'm not actually suggesting you should stop using "assign [super init] to self" initializers. Thank you to everyone who invented creative ways to tell me I was wrong about this implication.

You don't need to assign [super init] to self to make most classes work. In some obscure cases, it is actually the wrong thing to do.

So why do we continue to assign to self? It's the traditional template for an initializer, and although it's wrong in some cases, it is right in other cases which have been written to expect this approach.

Further to this is the consideration that class clusters and other classes likely to return unrelated or different, fully initialized objects from their init methods are not supposed to be subclassed in a normal way — making code which favors them less relevant.

The number of cases where super returns an unrelated object are so small that they can easily be dealt with on a case-by-case basis — a class will normally make it very clear when its initializers might return something other than the receiver or nil.

Showing a "Loading..." message over the iPhone keyboard

The "Text" (SMS) application on the iPhone uses a custom, semi-transparent view to show its "Sending..." message over the keyboard. I'll show you a simple class that can display semi-transparent loading messages and how you can display messages over the keyboard.

Introduction

"Loading..." messages

When waiting for data loaded from the internet, many iPhone applications use a mostly black, semi-transparent view to block the display. Most use a basic "spinner" (UIActivityIndicatorView) to reassure the user that the application is still running, frequently accompanied by "Loading..." text.

Despite the prevalence of this type of loading message, it is not a standard control and must be constructed manually.

Finding the keyboard

Apple give no methods to locate the keyboard or even the current first responder in an iPhone application. I'll show you how you can find both.

The sample application

The sample LoadingView application in this post can display the following two types of loading window:

loadingview.png

A full-window loading message and a keyboard-only loading message.

The sample application doesn't actually load anything. The "Refresh" button displays the full-window loading message for 5 seconds and the text field lets you enter some text and hit "Send" to see the keyboard-only loading message for 5 seconds.

Displaying a loading view

A loading view is not the most complicated piece of custom user-interface but there are a handful of common behaviors it should implement so it is a good idea to have a reusable class for the purpose.

The behaviors in my loading view include:

  • Always fill the whole view that it blocks (even though it looks inset on all sides for aesthetic reasons).
  • Fade in and fade out when added and removed.
  • Semi-transparent, allowing the unloaded view to show through.
  • Autoresizeable so that a portrait to landscape rotation during loading won't disrupt the display.
  • Displays a centered status message and activity indicator.

To ensure that these behaviors are applied to the view on construction, I use a loadingViewInView: method instead of a normal constructor. This method constructs, adds to superview and handles the fade animation all at once.

+ (id)loadingViewInView:(UIView *)aSuperview
{
    LoadingView *loadingView =
        [[[LoadingView alloc] initWithFrame:[aSuperview bounds]] autorelease];
    if (!loadingView)
    {
        return nil;
    }
    
    loadingView.opaque = NO;
    loadingView.autoresizingMask =
        UIViewAutoresizingFlexibleWidth |
        UIViewAutoresizingFlexibleHeight;
    [aSuperview addSubview:loadingView];

    // Code to create and configure the label and activity view goes here.
    // Download the sample project to see it.

    // Set up the fade-in animation
    CATransition *animation = [CATransition animation];
    [animation setType:kCATransitionFade];
    [[aSuperview layer] addAnimation:animation forKey:@"layerAnimation"];
    
    return loadingView;
}

All that's required to make it look semi-transparent is a custom drawing method.

- (void)drawRect:(CGRect)rect
{
    rect.size.height -= 1;
    rect.size.width -= 1;
    
    const CGFloat RECT_PADDING = 8.0;
    rect = CGRectInset(rect, RECT_PADDING, RECT_PADDING);
    
    const CGFloat ROUND_RECT_CORNER_RADIUS = 5.0;
    CGPathRef roundRectPath =
        NewPathWithRoundRect(rect, ROUND_RECT_CORNER_RADIUS);
    
    CGContextRef context = UIGraphicsGetCurrentContext();

    const CGFloat BACKGROUND_OPACITY = 0.85;
    CGContextSetRGBFillColor(context, 0, 0, 0, BACKGROUND_OPACITY);
    CGContextAddPath(context, roundRectPath);
    CGContextFillPath(context);

    const CGFloat STROKE_OPACITY = 0.25;
    CGContextSetRGBStrokeColor(context, 1, 1, 1, STROKE_OPACITY);
    CGContextAddPath(context, roundRectPath);
    CGContextStrokePath(context);

    CGPathRelease(roundRectPath);
}

Round Rects are Everywhere!

I continue to find it strange that Apple don't provide a function to draw a round rectangle in one line. They do provide the (more flexible) CGPathAddArcToPoint function but it lacks the simplicity of a single line function to handle the common case. Download the project and see how the implementation of NewPathWithRoundRect creates round rects using the CGPathAddArcToPoint function if you don't know how to draw round rects on the iPhone.

The absence of a round rectangle function is particularly strange given the anecdote that Andy Hertzfeld relates on folklore.org and in his excellent book Revolution in the Valley. In this anecdote, Steve Jobs drags Bill Atkinson on a walk around the block with Steve pointing out how everything is made of round rects until Bill relents and agrees to put the RoundRect function into Quickdraw.

Round rectangles continue to be everywhere on the iPhone — maybe more so than on the Mac in 1984. It's a good idea to use a function like NewPathWithRoundRect in your own code.

Finding the keyboard

The keyboard on the iPhone is an instance of UIKeyboard (a private class) in its own UIWindow (actually, it may share the window with the UIAutoCorrectInlineView).

You can find the UIKeyboard with a simple search.

@implementation UIApplication (KeyboardView)

- (UIView *)keyboardView
{
    NSArray *windows = [self windows];
    for (UIWindow *window in [windows reverseObjectEnumerator])
    {
        for (UIView *view in [window subviews])
        {
            if (!strcmp(object_getClassName(view), "UIKeyboard"))
            {
                return view;
            }
        }
    }
    
    return nil;
}

@end

The keyboard itself is a series of nested views which eventually reach the underlying functionality for the keys:

  • UIKeyboard
    • UIKeyboardImpl
      • UIKeyboardLayoutQWERTY
        • UIKeyboardSublayout
          • UIImageView
          • UIKeyboardSpaceKeyView
          • UIKeyboardReturnKeyView

Interestingly, the keys for the main part of the keyboard are all a single image. This arrangement probably explains why the "space" and "return" keys behave more like regular buttons than the other keys.

For the sample application, since the implementation of LoadingView will size the loading view to cover its immediate superview, passing the keyboard view fetched in this manner will create the keyboard-only loading view shown above.

If you wanted to show a full-window LoadingView that also covers the keyboard, you could pass the keyboard view's superview to the loadingViewInView: but you might want to add an extra 20 pixels padding at the top in this case, since the superview (the window) extends underneath the status bar at the top of the window.

Finding the firstResponder

While it isn't required for the sample project, I thought I'd mention how to fetch a related piece of information: the firstResponder in an iPhone application.

On the iPhone firstResponder is the view which has the current keyboard focus (or nil if no view is focussed).

This is an important piece of information, so it's strange that Apple didn't choose to provide a public method to access it. Curiously, there is a method, firstResponder, on UIWindow which returns this value but it isn't public. This will work:

UIView *firstResponder =
    [[UIApplication sharedApplication]
        keyWindow]
            performSelector:@selector(firstResponder)];

Conclusion

You can see all this code and more in the sample project for this post: LoadingView.zip (129kB)

Displaying a loading view is not a very difficult task (lots of people write their own) but implementing all of the different expected behaviors is time consuming — the implementation in this post is at least 65 lines of code, depending on how you count it — so keeping a resusable implementation can save a lot of time.

Finding the keyboard and finding the current first responder on the iPhone are harder to work out since the API is hidden, and it requires a little investigative work.

Putting it all together, you could easily recreate Apple's "Sending..." progress view used in the Text program.

8 Confusing Objective-C Warnings and Errors

Objective-C's unique syntax results in unique ways of making mistakes. In this post, I look at the compiler warnings and errors GCC outputs when you make mistakes or potential mistakes in your Objective-C syntax and show you how to fix them.

Every warning is an error

Objective-C lets a lot of potentially fatal problems related to method invocations go through as warnings. This is because the method lookup system is dynamic and from the compiler's perspective you could provide a resolution at runtime.

To make this overly permissive situation in Objective-C safer, you should treat every warning as a potentially fatal error. This means that you should turn on GCC_TREAT_WARNINGS_AS_ERRORS (-Werror) in your build settings so you can catch these problems and fix them instead of letting your program run until it crashes at runtime.

Every warning has an approach to eliminate the warning. These warning-less approaches invariably represent better design. I'll let you know how to fix each of the warnings/errors listed here.

1. Improperly nested method brackets

/path/file.m:15: error: syntax error before 'autorelease'

(where autorelease could be the first word in any method) or

/path/file.m:15: error: syntax error before ';' token

These are the only syntax errors I'm going to include in this list — my reason for including them is that, despite the obvious nature of the problem and its high occurrence rate, GCC presents the errors in a non-descript way that doesn't reveal the underlying problem.

The first error is "you are missing an opening bracket somewhere before this". The second error is "you are missing a closing bracket immediately before the semi-colon".

It would be great if GCC gave an error like "unmatched bracket" and pointed to the unmatched bracket. Apple are hinting that future clang-based compilers may do this but for now, you get one of these terse errors.

2. Trying to use a forward class

/path/file.m:22: warning: receiver 'Test' is a forward class and corresponding @interface may not exist

This is a simple error but many people (especially those accustomed to other languages that don't have separate declaration and implementation) find it confusing because they may not be certain what a "forward class" is.

I'd like to take this opportunity to tell you: if you don't know what a forward declaration is, or you've never used @class before, you need to read about it. In brief: a @class forward declaration tells the compiler that a given name is a class but avoids the need to import the whole declaration (which would create an undesirable cross-dependency in the header file).

Basic rule: in header files, never #import another class from the same framework/application except the super class — always use an @class forward definition instead. The #import for a class should always be in the implementation (.m) file. Using #import for other frameworks (like #import <Cocoa/Cocoa.h>) is okay.

The cause of this warning is failure to follow the second part of this basic rule: you need to #import the actual definition in your implementation file.

Older versions of GCC (prior to 4.0) didn't always give this warning. The result was that you could accidentally use classes that didn't exist (resulting in runtime crashes). This warning is a big improvement.

3. Recursive headers

/path/file.h:13: warning: duplicate interface declaration for class 'Test'
/path/file.h:15: error: redefinition of 'struct Test'

There are two ways to get this error. The first is mundane: you have declared two classes with the same name — not an Objective-C specific problem.

The second is that you have used #include to import the header declaration instead of #import. This is especially recognizable when you see the error dozens (or hundreds) of times in a row, especially if it is further accompanied by:

/path/file.h:9:35: error: #include nested too deeply

Objective-C expects that all header files are imported using #import which prevents recursive inclusion, so files don't guard against repeat inclusion as they normally do in standard C.

Solution: always use #import to import header declarations (and don't listen to Richard Stallman's rants against the #import keyword — he is... different).

4. Interface not imported

/path/file.m:26: warning: no '-blah' method found
/path/file.m:26: warning: (Messages without a matching method signature
/path/file.m:26: warning: will be assumed to return 'id' and accept
/path/file.m:26: warning: '...' as arguments.)

The mundane explanation is that there is no blah method (you've simply mistyped it).

However, Objective-C provides a few ways to get this error, even when the method does exist.

  • The class where -blah is defined may not be in the set of imported classes. This can happen when the object you tried to invoke -blah on is declared as an id or the class is only forward declared.
  • You have imported the base class but the method is declared on a @category, which is not imported.

In either case, the solution is to find the header that defines the method and #import it.

There is one further situation where this error can occur: when there is no implementation of the method at compile-time at all. This can further be broken into two cases:

  • Runtime handled methods with clear association with a particular class. For example: accessor methods for the attributes of a Core Data NSManagedObjects. For methods like this, you should declare the method in a category (even though there will be no implementation at compile-time) and import this category.
  • Runtime handled methods with no clear class association. To highlight the runtime and unusual situation involved in this situation you should use [object performSelector:@selector(weirdRuntimeMethod)] instead of writing [object weirdRuntimeMethod] and causing compiler warnings.

5. Multiple, incompatible methods

/path/file.m:24: warning: multiple methods named '-setStringValue:' found

Normally, Objective-C won't complain if there are multiple methods matching a given name (it assumes runtime lookup will work it out) so this warning may be surprising when it occurs.

This type of problem occurs when you have multiple declarations with the same method name but the arguments to the method are different sizes (e.g. one declaration expects a 32-bit long parameter and one declaration expects a 64-bit long parameter). This is important because parameter sizes are fixed at compile-time (runtime lookup can't change it).

The solution is to tell the compiler which method is the correct method by casting your object to the exact class involved. For example:

[(NSXMLNode *)myObject setStringValue:@"value"];

or

NSXMLNode *myXMLNodeObject = myObject;
[myXMLNodeObject setStringValue:@"value"];

Both of these solutions produce the same compiled output.

6. Accessing a property on the wrong type

/path/file.m:23: error: request for member 'value' in something not a structure or union

This error can occur in standard C code when using struct or union as the error reports.

In Objective-C 2.0, it also occurs when accessing an Objective-C 2.0 property on a class and the compiler can't find the property.

If this error occurs and you have typed the name correctly, you probably need to cast the object to the correct class. i.e. if value is a property of MyClass:

id value = ((MyClass *)object).value;

or

MyClass *myClassObject = object;
id value = myClassObject.value;

These solutions are, as before, both equivalent.

The other potential cause of this error is that MyClass is not imported correctly, so be certain to check this as well.

7. Implicit downcasting on assignment

/path/file.m:22: warning: initialization from distinct Objective-C type

The mundane cause of this error is that you've tried to assign the return value of a method to an unrelated object type. That's just an error and you need to fix it.

The trickier case is an implicit downcast. By this I mean: the method has returned a super class (like NSObject) but you know it is actually a child class (like MyClass).

In this situation you must explicitly cast:

MyClass *myClassObject = (MyClass *)[someObject getObject];

C++ has the dynamic_cast operator for this type of action, which verifies that myClassObject is correctly a MyClass object. In Objective-C if you're concerned about runtime type, you should use:

NSAssert([myClassObject isKindOfClass:[MyClass class]],
    @"Return value is not of type MyClass as expected.");

You can use your own AssertCast macro to make this operation easier if you do it a lot.

There are two cases where implict casts are allowed:

  • upcasts (i.e. NSObject *myObject = myClassObject; is okay)
  • implicit conversions from id to anything (id is the universal object and can be implicitly cast or used as anything)

This error can also occur in the case where the assignment is actually an upcast but the source class has only a forward declaration — so you may also need to import the declaration if it is not imported.

8. Implicit downcasting of parameters

/path/file.m:24: warning: passing argument 1 of 'test:' from distinct Objective-C type

The causes and solutions for this are identical to downcasting on assignment. Cast correctly and import declarations as appropriate.

I know, this warning seems like a duplicate of number 7 but it's a programming blog: I had to have 8 things. In my head, I've stored them all in an array indexed from 0 to 7.

Conclusion

Never be content with warnings in your code. Always work to understand why GCC is issuing the warning and fix your code. Your code will be easier to understand and be safer at runtime.