Objective-C Conventions

Writing code without conforming to some form of convention lends itself to a lot of confusion; Both for the code writer, and for someone else trying to understand your code. The pain worsens as the code base starts to increase in size, especially for a non-GC programming languages like Objective-C where memory management has to be cared for meticulously. Following a set of naming conventions for your instance variables, class names…etc. will help maintain a certain level of sanity. Here at Miso, we conform to a set of conventions that are partly derived from Apple standards, and partly from common best practices we’ve seen from other developers.

Class and Variable names

For variable names, Apple recommends starting with a lowercase letter (eg. UILabel *titleLabel). For class names, starting with a uppercase letter (eg. MyClass). Notice in the examples we also like to camel case the rest of the name.

iVars and local variables

One of the most common issues with a large class or viewController containing more than several instance variables is distinguishing them from a local variable within a function. What I like to do is prefix iVars with an underscore to distinguish them. (eg. _titleLabel)

- (void)createViews {
    // some bunch of code above
    UIImageView *imageView = [[[UIImageView alloc] initWithImage:[UIImage imageNamed:@"somepic.png"]] autorelease];

    [_containerView addSubview:imageView];
    [_containerView addSubview:titleLabel];
    // some more code below
}

In this example you can see that I don’t have to look at the header file to immediately tell that imageView and titleLabel are a locally defined variables, and that _containerView is an iVar simply looking at the name of the variables.

Starting with the Header File

When I approach designing a new class I like to start with the header file first. This is where you wireframe your code design without actually writing any implementation. Reason for this is because I like to start with what someone using this class would need. This means defining public class/instance methods, properties (public accessors), and instance variables. Let’s try this approach with an example. I love cats, so let’s go with that:

//
//  VirtualCat.h
//  Miso
//
//  Created by Joshua Wu on 8/12/11.
//  Copyright 2011 Miso. All rights reserved.
//

#import <Foundation/Foundation.h>

@interface VirtualCat : NSObject {
    BOOL _hungry;
    NSMutableArray *_foodHistory;
    NSString *_furColor;
    float _weight;
}

@property (nonatomic, readonly) float weight;
@property (nonatomic, retain) NSString *furColor;

- (id)initWithColor:(NSString *)furColor weight:(float)weight;
- (void)feed:(NSString *)food;

@end

Ok great! Here you can see that certain properties of this class is can be modified after instantiating. (furColor and weight) We also see that certain properties can’t be modified because public accessors were not defined. (hungry and foodHistory). I’ve also defined a constructor, and a instance method “feed”. For someone using this class, what they can do with this class is all nicely defined in the header file.

Private Interfaces

Using properties is great because you can use the accessors you get from it to assign new values to them without worrying about memory management. This is great for public properties, but what if I want to define private accessors to leverage the same convenience in my implementation file? This is what we can do:

//
// VirtualCat.m
// Miso
//
// Created by Joshua Wu on 8/12/11.
// Copyright 2011 Miso. All rights reserved.
//

#import "VirtualCat.h"

@interface Cat()

@property (nonatomic, retain) NSMutableArray *foodHistory;

- (void)askForFood;
- (void)sleepInBathTub;
- (void)meow;

@end

@implementation VirtualCat
@synthesize weight=_weight;
@synthesize furColor=_furColor;
@synthesize foodHistory=_foodHistory;

@end

By declaring an interface within the implementation file, you can assign private properties. I’ve also sneaked in some additional code in the private interface to demonstrate defining private instance methods. So far, we’ve done enough to define the blueprint of this class quite sufficiently without having written any implementation code! Yet, it is already clear what someone instantiating this class would be able to do with it, and what I need to implement. This gives me a good picture of what functions and variables I’ve defined when I come back to this code in the future. If you were in XCode looking at this class, it’d also conveniently give you a compiler warning saying that your implementation file is incomplete (Incomplete implementation of class). This is a good guideline for me to make sure I’ve implemented all the functions I intended to as I work through the implementation.

When it all comes together…

Alright, great! Let’s have some fun and finish off the implementation using the conventions I’ve defined earlier. Enjoy!

//
//  VirtualCat.m
//  Miso
//
//  Created by Joshua Wu on 8/12/11.
//  Copyright 2011 Miso. All rights reserved.
//

#import "VirtualCat.h"

@interface VirtualCat()

@property (nonatomic, retain) NSMutableArray *foodHistory;

- (void)askForFood;
- (void)sleepInBathTub;
- (void)meow;

@end

@implementation VirtualCat
@synthesize weight=_weight;
@synthesize furColor=_furColor;
@synthesize foodHistory=_foodHistory;

- (id)initWithColor:(NSString *)furColor weight:(float)weight {
    if((self = [super init])) {
        // Primitive iVars so I don't bother defining properties for them
        _weight = weight;
        _hungry = YES;
        
        // Using self. accessors to maintain memory santiy.
        // Less sane alternative would be eg. _foodHistory = [[NSMutableArray array] retain]
        self.furColor = furColor;
        self.foodHistory = [NSMutableArray array];
    }
    
    return self;
}

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

#pragma mark - public methods

- (void)feed:(NSString *)food {
    if ([food isEqualToString:@"Can Food"]) {
        NSLog(@"Om nom nom nom");
    } else if ([food isEqualToString:@"Dry Food"]) {
        NSLog(@"Om nom");
    } else {
        NSLog(@"Not eating this");
    }
    
    [_foodHistory addObject:food];
    _hungry = NO;
    _weight += 1;
    
    [self sleepInBathTub];
}

#pragma mark - private methods

- (void)askForFood {
    _hungry = YES;
    [self meow];
}
          
- (void)meow {
    _weight -= 0.1;
    
    if (_hungry && _weight > 0) {
        NSLog(@"MEOW!");
        [self performSelector:@selector(meow) withObject:nil afterDelay:2];
    } else if (!_hungry && weight > 0) {
        NSLog(@"Purrr~");
    }
        NSLog(@"You allowed me to die damn it! (╯‵Д′)╯彡┻━┻");
    }
}

- (void)sleepInBathTub {
    NSLog(@"zzzzzz");
    [self performSelector:@selector(askForFood) withObject:nil afterDelay:1000];
}

@end

Pragma Marking

One more thing I’d like to suggest is use pragma marks to section off method types in your code. (eg. #pragma mark – private methods) This allows xcode to nicely section off methods in their quick access menus.

Suggestions!

By now, it should be evident how following conventions when coding can be very beneficial. This is by no means THE standard to follow, but one that I feel has helped me stay sane as I develop. If you have your own conventions that you feel would add value, I’d love to hear from you and exchange ideas on this topic.

This entry was posted in All, Engineering. Follow any comments here with the RSS feed for this post. Post a comment or leave a trackback: Trackback URL.

Add a Comment

Your email is never published nor shared.

*
*

3 Comments

  1. everything written down here is useful only in modern runtime (iOS4+):

    1. Apple engineers sad a couple of times during the WWDC session that you should not name your instance variables with underscore.

    2. do not call instance variables directly – always use a property, if it is atomic – compiler will inline them. You should only call them in getter/setter if you need a @dynamic property.

    3. Header file should contain only public properties and methods. Everything else should be moved to class extension.

    3. if you defined a property as @synthesize there is no needs to define an instance variable.

    4. if you defined a property as @dynamic you still need define a instance variable. But because we don’t do that in header file we can do it in class extension:

    @interface Cat() {
    BOOL someBool;
    }

    @end

    5. about dealloc:
    this is wrong:

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

    it should be at least this:
    - (void)dealloc {
    [_foodHistory release], _foodHistory = nil;
    [_furColor release], _furColor = nil;
    [super dealloc];
    }

    but because we don’t call instance variables directly it should be:

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

  2. and always, ALWAYS use a two letter prefix for class name

  3. When it comes to sunglasses, the words fake and replica often mean the same to a lot of people, but the truth is, a fake Oakley and a replica Oakley are actually different from each other. While these two things have something in common, which is their being copies of the popular eyewear name, they are actually very different in terms of what and how they really are made. http://www.soaho-fakeoakleys.com/