Skip to content

wangshengjia/-Code-Style---Best-Practise-for-Objective-C

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

10 Commits
 
 
 
 
 
 

Repository files navigation

Code Style & Best Practise for Objective-C

  • Language
  • Code Naming
  • Method Signatures
  • Variables
  • Expressions
  • Spacing
  • Conditionals
  • Ternary Operator
  • Private Properties/Methods
  • Organization
  • Singletons
  • CGRect Functions
  • Constants
  • Literals
  • Init Methods
  • Dealloc
  • Booleans
  • Enumerated Types
  • Error handling
  • Golden Path
  • Refactor and comment on your method and class
  • Basic Code Principles

##Language

  • US-English is the language par default.

Preferred:

UIColor *myColor = [UIColor whiteColor];

Not Preferred:

UIColor *monColour = [UIColor whiteColor];

##Code Naming

Apple Official Naming Guide

Preferred: Clear, expressive, non-ambiguous names. Since you do far more reading of code than writing, invest the time to type an extra eight characters (and then leverage autocomplete).

Not Preferred:

  • Hungarian notation e.g. data type as part of name strUserName
    Exception: const or enum values should follow Apple's class-name-then-least-to-most specific pattern. e.g. UIDeviceOrientationLandscapeRight or VPNavigationBarHeight_iPad).

  • Abbreviating. Lazy naming. e.g index What index? For an array of house objects use houseIndex

##Method Signatures

Preferred:

- (void)setExampleText:(NSString *)text image:(UIImage *)image;
- (void)sendAction:(SEL)aSelector to:(id)anObject forAllCells:(BOOL)flag;
- (id)viewWithTag:(NSInteger)tag;
- (instancetype)initWithWidth:(CGFloat)width height:(CGFloat)height;

Not Preferred:

-(void)setT:(NSString *)text i:(UIImage *)image;
- (void)sendAction:(SEL)aSelector :(id)anObject :(BOOL)flag;
- (id)taggedView:(NSInteger)tag;
- (instancetype)initWithWidth:(CGFloat)width andHeight:(CGFloat)height;
- (instancetype)initWith:(int)width and:(int)height;

Preferred:

answerViewController

Not Preferred:

answer_view_controller
  • For methods that represent an action an object takes, start the name with the action.

Preferred:

- (IBAction)showDetailViewController:(id)sender

Not Preferred:

- (void)detailButtonTapped:(id)sender

If the method returns an attribute of the receiver, name the method after the attribute. The use of "get" is unnecessary, unless one or more values are returned indirection.

Preferred:

- (NSInteger)age

Not Preferred:

- (NSInteger)calcAge
- (NSInteger)getAge

##Variables

  • Use @property without @synthesized is always preferred in most cases in stead of using directly instance variable.

Direct instance variable access should be avoided except one exception to this: inside initializers, the backing instance variable (i.e. _variableName) should be used directly to avoid any potential side effects of the getters/setters.

  • Local variables should not contain underscores.

  • Use always NSInteger/NSUInteger instead of int. Use CGFloat instead float.

##Expressions

  • Dot-notation should always be used for accessing and mutating properties. Bracket notation is preferred in all other instances.

Preferred:

NSInteger arrayCount = [self.array count];
view.backgroundColor = [UIColor orangeColor];
[UIApplication sharedApplication].delegate;

Not Preferred:

NSInteger arrayCount = self.array.count;
[view setBackgroundColor:[UIColor orangeColor]];
UIApplication.sharedApplication.delegate;

##Spacing

  • Indent using 4 spaces. Never indent with tabs. Be sure to set this preference in IDE ( Xcode/Appcode ).

##Conditionals

  • Method braces and other braces (if/else/switch/while etc.) always open on the same line as the statement but close on a new line.
  • should always use braces

Preferred:

if (condition) {
    // do stuff
} else {
    // do other stuff
}

##Ternary Operator Preferred:

NSInteger value = 5;
result = (value != 0) ? x : y;

BOOL isHorizontal = YES;
result = isHorizontal ? x : y;

Not Preferred:

result = a ]]> b ? x = c ]]> d ? c : d : y;

##Private Properties/Methods

  • Private properties should be declared in class extensions (anonymous categories) in the implementation file of a class. Named categories (such as RWTPrivate or private) should never be used unless extending another class. The Anonymous category can be shared/exposed for testing using the +Private.h file naming convention.

Preferred:

@interface RWTDetailViewController ()

@property (strong, nonatomic) GADBannerView *googleAdView;
@property (strong, nonatomic) ADBannerView *iAdView;
@property (strong, nonatomic) UIWebView *adXWebView;

@end

##Organization

  • use #pragma mark -s to categorize methods in functional groupings and protocol/delegate implementations following this general structure.

Preferred:

#pragma mark - Lifecycle
+ (instancetype)objectWithThing:(id)thing {}
- (instancetype)init {}
- (void)dealloc {}
- (void)viewDidLoad {}
- (void)didReceiveMemoryWarning {}

#pragma mark - Custom Accessors

- (void)setCustomProperty:(id)property {}
- (id)anotherCustomProperty {}

#pragma mark - Actions

- (IBAction)submitData:(id)sender {}

#pragma mark - Public

- (void)publicMethod {}

#pragma mark - Private

- (void)privateMethod {}

#pragma mark - Protocol conformance
#pragma mark - UITextFieldDelegate
#pragma mark - UITableViewDataSource
#pragma mark - UITableViewDelegate

#pragma mark - NSCopying

- (id)copyWithZone:(NSZone *)zone {}

#pragma mark - NSObject

- (NSString *)description {}

##Singletons

Preferred:

+ (instancetype)sharedInstance {  static id sharedInstance = nil;
  static dispatch_once_t onceToken;
  dispatch_once(&onceToken, ^{
      sharedInstance = [[self alloc] init];
  });
  return sharedInstance;
}

CGRect Functions

When accessing the x, y, width, or height of a CGRect, always use the CGGeometry functions instead of direct struct member access. From Apple's CGGeometry reference:

All functions described in this reference that take CGRect data structures as inputs implicitly standardize those rectangles before calculating their results. For this reason, your applications should avoid directly reading and writing the data stored in the CGRect data structure. Instead, use the functions described here to manipulate rectangles and to retrieve their characteristics.

Preferred:

CGRect frame = self.view.frame;

CGFloat x = CGRectGetMinX(frame);
CGFloat y = CGRectGetMinY(frame);
CGFloat width = CGRectGetWidth(frame);
CGFloat height = CGRectGetHeight(frame);
CGRect frame = CGRectMake(0.0, 0.0, width, height);

Not Preferred:

CGRect frame = self.view.frame;

CGFloat x = frame.origin.x;
CGFloat y = frame.origin.y;
CGFloat width = frame.size.width;
CGFloat height = frame.size.height;
CGRect frame = (CGRect){ .origin = CGPointZero, .size = frame.size };

Constants

Constants are preferred over in-line string literals or numbers, as they allow for easy reproduction of commonly used variables and can be quickly changed without the need for find and replace. Constants should be declared as static constants and not #defines unless explicitly being used as a macro.

Preferred:

static NSString * const VSCAboutViewControllerCompanyName = @"voyages-sncf.com";

static CGFloat const VSCImageThumbnailHeight = 50.0;

Not Preferred:

#define CompanyName @"voyages-sncf.com"

#define thumbnailHeight 2

Literals

NSString, NSDictionary, NSArray, and NSNumber literals should be used whenever creating immutable instances of those objects. Pay special care that nil values can not be passed into NSArray and NSDictionary literals, as this will cause a crash.

Preferred:

NSArray *names = @[@"Brian", @"Matt", @"Chris", @"Alex", @"Steve", @"Paul"];
NSDictionary *productManagers = @{@"iPhone": @"Kate", @"iPad": @"Kamal", @"Mobile Web": @"Bill"};
NSNumber *shouldUseLiterals = @YES;
NSNumber *buildingStreetNumber = @10018;

Not Preferred:

NSArray *names = [NSArray arrayWithObjects:@"Brian", @"Matt", @"Chris", @"Alex", @"Steve", @"Paul", nil];
NSDictionary *productManagers = [NSDictionary dictionaryWithObjectsAndKeys: @"Kate", @"iPhone", @"Kamal", @"iPad", @"Bill", @"Mobile Web", nil];
NSNumber *shouldUseLiterals = [NSNumber numberWithBool:YES];
NSNumber *buildingStreetNumber = [NSNumber numberWithInteger:10018];

Init Methods

Init methods should follow the convention provided by Apple's generated code template. A return type of 'instancetype' should also be used instead of 'id'.

More information from Apple doc: Adopting Modern Objective-C

- (instancetype)init {
  self = [super init];
  if (self) {
    // ...
  }
  return self;
}

Dealloc

Dealloc methods are no longer required when using arc but in certain cases must be used to remove observers, KVO, etc.

Preferred:

- (void)dealloc{
    [[NSNotificationCenter defaultCenter] removeObserver:self];
}

Booleans

Objective-C uses YES and NO. Therefore true and false should only be used for CoreFoundation, C or C++ code. Since nil resolves to NO it is unnecessary to compare it in conditions. Never compare something directly to YES, because YES is defined to 1 and a BOOL can be up to 8 bits.

This allows for more consistency across files and greater visual clarity.

More information about boolean

Preferred:

if (someObject) {}
if (![anotherObject boolValue]) {}

Not Preferred:

if (someObject == nil) {}
if ([anotherObject boolValue] == NO) {}
if (isAwesome == YES) {} // Never do this.
if (isAwesome == true) {} // Never do this.

If the name of a BOOL property is expressed as an adjective, the property can omit the “is†prefix but specifies the conventional name for the get accessor, for example:

@property (assign, getter=isEditable) BOOL editable;

Enumerated Types

When using enums, it is recommended to use the new fixed underlying type specification because it has stronger type checking and code completion. The SDK now includes a macro to facilitate and encourage use of fixed underlying types: NS_ENUM() More information from Apple doc: Adopting Modern Objective-c For Example:

typedef NS_ENUM(NSInteger, RWTLeftMenuTopItemType) {
  RWTLeftMenuTopItemMain,
  RWTLeftMenuTopItemShows,
  RWTLeftMenuTopItemSchedule
};

You can also make explicit value assignments (showing older k-style constant definition):

typedef NS_ENUM(NSInteger, RWTGlobalConstants) {
  RWTPinSizeMin = 1,
  RWTPinSizeMax = 5,
  RWTPinCountMin = 100,
  RWTPinCountMax = 500,
};

Older k-style constant definitions should be avoided unless writing CoreFoundation C code (unlikely).

Not Preferred:

enum GlobalConstants {
  kMaxPinSize = 5,
  kMaxPinCount = 500,
};

Golden Path

Preferred:

- (void)someMethod {
  if (![someOther boolValue]) {
        return;
  }

  //Do something important
}

Not Preferred:

- (void)someMethod {
  if ([someOther boolValue]) {
    //Do something important
  }
}

Error handling

When methods return an error parameter by reference, switch on the returned value, not the error variable.

Preferred:

NSError *error;
if (![self trySomethingWithError:&error]) {
  // Handle Error
}

Not Preferred:

NSError *error;
[self trySomethingWithError:&error];
if (error) {
  // Handle Error
}

Refactor and comment on your method and class

  • A method should be less than 50 lines.
  • A class should be less than 500 lines.
  • Add comment for the important method. Xcode plugin - VVDocumenter

Preferred:

/**
 *  <#Description#>
 *
 *  @param image    <#image description#>
 *  @param fileName <#fileName description#>
 *
 *  @return <#return value description#>
 *
 *  @since <#version number#>
 */
- (BOOL)saveToImage:(UIImage *)image withFileName:(NSString *)fileName {
    NSString *archivesDirectory = [self applicationArchivesDirectory];
    if (!archivesDirectory) return NO;
 
    // Create Path
    NSString *filePath = [archivesDirectory stringByAppendingPathComponent:fileName];
 
    // Write Image to Disk
    return [UIImageJPEGRepresentation(image, 8.0) writeToFile:filePath atomically:YES];
}

- (NSString *)applicationDocumentsDirectory {
    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    return paths.count ? [paths objectAtIndex:0] : nil;
}

- (NSString *)applicationArchivesDirectory {
    NSString *documentsDirectory = [self applicationDocumentsDirectory];
    NSString *archivesDirectory = [documentsDirectory stringByAppendingPathComponent:@"Archives"];
 
    NSFileManager *fm = [NSFileManager defaultManager];
    if (![fm fileExistsAtPath:archivesDirectory]) {
        NSError *error = nil;
        [fm createDirectoryAtPath:archivesDirectory withIntermediateDirectories:YES attributes:nil error:&error];
        if (error) {
            NSLog(@"Unable to create directory due to error %@ with user info %@.", error, error.userInfo);
            return nil;
        }
    }
    return archivesDirectory;
}

Not Preferred:

- (BOOL)saveToImage:(UIImage *)image withFileName:(NSString *)fileName {
    BOOL result = NO;
    NSString *documents = nil;
    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
 
    if (paths.count) {
        documents = [paths objectAtIndex:0];
        NSString *basePath = [documents stringByAppendingPathComponent:@"Archive"];
 
        if (![[NSFileManager defaultManager] fileExistsAtPath:basePath]) {
            NSError *error = nil;
            [[NSFileManager defaultManager] createDirectoryAtPath:basePath withIntermediateDirectories:YES attributes:nil error:&error];
 
            if (!error) {
                NSString *filePath = [basePath stringByAppendingPathComponent:fileName];
                result = [UIImageJPEGRepresentation(image, 8.0) writeToFile:filePath atomically:YES];
 
            } else {
                NSLog(@"Unable to create directory due to error %@ with user info %@.", error, error.userInfo);
            }
        }
    }
 
    return result;
}

##Basic Code Principles

  • Each function/method should aim to perform one action/task that reflects it's name.
  • Since each function/method performs one action/task each one should be relatively short. If the code does not fit on one screen for example, you know you have a problem!
  • Declare local variables as close to the code they are used in as possible.
  • Always aim to reduce code nesting (ie a statement that is nested in an if, an if, a for and an if is hard for the brain to evaluate. Refactor!).

About

Code Style & Best Practise for Objective-C

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published