• Object-C編碼規(guī)范

    參考資料:
    • Apple: Coding Guidelines for Cocoa
    • Google: Objective-C Style Guide
    • Three20: Source code style guildelines
    正文:
    • 格式化代碼
    ◦ 指針“*”號的位置
    ▪ 如:NSString *varName;
    ◦ 空格 VS tabs
    ▪ 只允許使用空格,將編輯器設(shè)置為1個TAB = 2個字符縮進
    ◦ 每行的長度
    ▪ 每行最多不得超過100個字符
    ▪ 以15寸Macbook Pro的大小,每行100個字符時能最大化地同時容下編輯器和iPhone模擬器
    ▪ Google的80字符的標準有點少,這導(dǎo)致過于頻繁的換行(Objectve-C的代碼一般都很長)
    ▪ 通過 “Xcode => Preferences => TextEditing => 勾選Show Page Guide / 輸入
    100 => OK” 來設(shè)置提醒
    ◦ 方法的聲明和定義
    ▪ 在 – OR + 和返回值之間留1個空格,方法名和第一個參數(shù)間不留空格。如:
    - (void)doSomethingWithString:(NSString *)theString {

    }
    ▪ 當參數(shù)過長時,每個參數(shù)占用一行,以冒號對齊。如:
    - (void)doSomethingWith:(GTMFoo *)theFoo
    rect:(NSRect)theRect
    interval:(float)theInterval {

    }
    ▪ 如果方法名比參數(shù)名短,每個參數(shù)占用一行,至少縮進4個字符,且為垂直對齊(而非使用冒號
    對齊)。如:
    - (void)short:(GTMFoo *)theFoo
    longKeyword:(NSRect)theRect
    evenLongerKeyword:(float)theInterval {

    }
    ◦ 方法的調(diào)用
    ▪ 調(diào)用方法沿用聲明方法的習(xí)慣。例外:如果給定源文件已經(jīng)遵從某種習(xí)慣,繼續(xù)遵從那種習(xí)慣。
    ▪ 所有參數(shù)應(yīng)在同一行中,或者每個參數(shù)占用一行且使用冒號對齊。如:
    [myObject doFooWith:arg1 name:arg2 error:arg3];

    [myObject doFooWith:arg1
    name:arg2
    error:arg3];
    ▪ 和方法的聲明一樣,如果無法使用冒號對齊時,每個參數(shù)一行、縮進4個字符、垂直對其(而非
    使用冒號對齊)。如:
    [myObj short:arg1
    longKeyword:arg2
    evenLongerKeyword:arg3];
    ◦ @public 和 @private
    ▪ @public 和 @private使用單獨一行,且縮進1個字符
    ◦ Protocals
    ▪ 類型標示符、代理名稱、尖括號間不留空格。
    ▪ 該規(guī)則同樣適用于:類聲明、實例變量和方法聲明。如:
    @interface MyProtocoledClass : NSObject<NSWindowDelegate> {
    @private
    id<MyFancyDelegate> _delegate;
    }
    - (void)setDelegate:(id<MyFancyDelegate>)aDelegate;
    @end
    ▪ 如果類聲明中包含多個protocal,每個protocal占用一行,縮進2個字符。如:
    @interface CustomViewController : ViewController<
    AbcDelegate,
    DefDelegate
    > {

    }
    • 命名
    ◦ 類名
    ▪ 類名(及其category name 和 protocal name)的首字母大寫,寫使用首字母大寫的形式
    分割單詞
    ▪ 在面向特定應(yīng)用的代碼中,類名應(yīng)盡量避免使用前綴,每個類都使用相同的前綴影響可讀性。
    ▪ 在面向多應(yīng)用的代碼中,推薦使用前綴。如:GTMSendMessage
    ◦ Category Name
    ▪ 待完善
    ◦ 方法名
    ▪ 方法名的首字母小寫,且使用首字母大寫的形式分割單詞。方法的參數(shù)使用相同的規(guī)則。
    ▪ 方法名+參數(shù)應(yīng)盡量讀起來像一句話(如:)。在這里查看蘋果對方法命名的規(guī)范。
    ▪ getter的方法名和變量名應(yīng)相同。不允許使用“get”前綴。如:
    - (id) getDelegate; // 禁止
    - (id)delegate; // 對頭
    ▪ 本規(guī)則僅針對Objective-C代碼,C++代碼使用C++的習(xí)慣
    ◦ 變量名
    ▪ 變量名應(yīng)使用容易意會的應(yīng)用全稱,且首字母小寫,且使用首字母大寫的形式分割單詞
    ▪ 成員變量使用“_”作為前綴(如:“NSString *_varName;”。雖然這與蘋果的標準(使
    用“_”作為后綴)相沖突,但基于以下原因,仍使用“_”作為前綴。
    ▪ 使用“_”作為前綴,更容易在有代碼自動補全功能的IDE中區(qū)分“屬性
    (self.userInfo)”和“成員變量(_userInfo)”
    ▪ 常量(#define, enums, const等)使用小寫“k”作為前綴,首字母大寫來分割單詞。如:
    kInvalidHandle
    • 注釋
    ◦ 待完善
    • Cocoa 和 Objective-C特有的規(guī)則
    ◦ 成員變量使用 @private。如:
    @interface MyClass : NSObject {
    @private
    id _myInstanceVariable;
    }
    // public accessors, setter takes ownership
    - (id)myInstanceVariable;
    - (void)setMyInstanceVariable:(id)theVar;
    @end
    ◦ Indentify Designated Initializer
    ▪ 待完善
    ◦ Override Desingated Initializer
    ▪ 待完善
    ◦ 初始化
    ▪ 在初始化方法中,不要將變量初始化為“0”或“nil”,那是多余的
    ▪ 內(nèi)存中所有的新創(chuàng)建的對象(isa除外)都是0,所以不需要重復(fù)初始化為“0”或“nil”
    ◦ 避免顯式的調(diào)用 +new 方法
    ▪ 禁止直接調(diào)用 NSObject 的類方法 +new,也不要在子類中重載它。使用alloc和init方法
    ◦ 保持公共API的簡潔性
    ▪ 待完善
    ◦ #import VS #include
    ▪ 使用 #import 引入Ojbective-C和Ojbective-C++頭文件,使用 #include 引入C和C++頭
    文件
    ◦ import根框架(root frameworks),而非各單個文件
    ▪ 雖然有時我們僅需要框架(如Cocoa 或 Foundation)的某幾個頭文件,但引入根文件編譯
    器會運行的更快。因為根框架(root frameworks)一般會預(yù)編譯,所以加載會更快。再次強
    調(diào):使用 #import 而非 #include 來引入Objective-C框架。如:
    #import <Foundation/NSArray.h> // 禁止
    #import <Foundation/NSString.h>

    #import <Foundation/Foundation.h> // 對頭
    ◦ 創(chuàng)建對象時盡量使用autorelease
    ▪ 創(chuàng)建臨時對象時,盡量同時在同一行中 autorelease 掉,而非使用單獨的 release 語句
    ▪ 雖然這樣會稍微有點慢,但這樣可以阻止因為提前 return 或其他意外情況導(dǎo)致的內(nèi)存泄露。
    通盤來看這是值得的。如:
    // 避免這樣使用(除非有性能的考慮)
    MyController* controller = [[MyController alloc] init];
    // … 這里的代碼可能會提前return …
    [controller release];
    // 這樣更好
    MyController* controller = [[[MyController alloc] init] autorelease];
    ◦ 先autorelease,再retain
    ▪ 在為對象賦值時,遵從“先autorelease,再retain”
    ▪ 在將一個新創(chuàng)建的對象賦給變量時,要先將舊對象release掉,否則會內(nèi)存泄露。市面上有很
    多方法來handle這種情況,這里選擇“先autorelease,再retain”的方法,這種方法不易引
    入error。注意:在循環(huán)中這種方法會“填滿”autorelease pool,稍稍影響效率,但是
    Google和我(  )認為這個代價是可以接受的。如:
    - (void)setFoo:(GMFoo *)aFoo {
    [foo_ autorelease]; // 如果foo_和aFoo是同一個對象(foo_ == aFoo),
    dealloc不會被調(diào)用
    foo_ = [aFoo retain];
    }
    ◦ dealloc的順序要與變量聲明的順序相同
    ▪ 這有利于review代碼
    ▪ 如果dealloc中調(diào)用其他方法來release變量,將被release的變量以注釋的形式標注清楚
    ◦ NSString的屬性的setter使用“copy”
    ▪ 禁止使用retain,以防止意外的修改了NSString變量的值。如:
    - (void)setFoo:(NSString *)aFoo {
    [foo_ autorelease];
    foo_ = [aFoo copy];
    }

    @property (nonatomic, copy) NSString *aString;
    ◦ 避免拋出異常(Throwing Exceptions)
    ▪ 待完善
    ◦ 對 nil 的檢查
    ▪ 僅在有業(yè)務(wù)邏輯需求時檢查 nil,而非為了防止崩潰
    ▪ 向 nil 發(fā)送消息不會導(dǎo)致系統(tǒng)崩潰,Objective-C運行時負責處理。
    ◦ BOOL陷阱
    ▪ 將int值轉(zhuǎn)換為BOOL時應(yīng)特別小心。避免直接和YES比較
    ▪ Objective-C中,BOOL被定義為unsigned char,這意味著除了 YES (1) 和 NO (0)外它
    還可以是其他值。禁止將int直接轉(zhuǎn)換(cast or convert)為BOOL。
    ▪ 常見的錯誤包括:將數(shù)組的大小、指針值或位運算符的結(jié)果轉(zhuǎn)換(cast or convert)為
    BOOL,因為該BOOL值的結(jié)果取決于整型值的最后一位
    ▪ 將整型值轉(zhuǎn)換為BOOL的方法:使用三元運算符返回YES / NO,或使用位運算符(&&, ||, !)
    ▪ BOOL、_Bool和bool之間的轉(zhuǎn)換是安全的,但是BOOL和Boolean間的轉(zhuǎn)換不是安全的,所以
    將Boolean看成整型值。
    ▪ 在Objective-C中,只允許使用BOOL
    ▪ 如:
    // 禁止
    - (BOOL)isBold {
    return [self fontTraits] & NSFontBoldTrait;
    }
    - (BOOL)isValid {
    return [self stringValue];
    }
    // 對頭
    - (BOOL)isBold {
    return ([self fontTraits] & NSFontBoldTrait) ? YES : NO;
    }
    - (BOOL)isValid {
    return [self stringValue] != nil;
    }
    - (BOOL)isEnabled {
    return [self isValid] && [self isBold];
    }
    ▪ 禁止直接將BOOL和YES/NO比較,如:
    // 禁止
    BOOL great = [foo isGreat];
    if (great == YES)

    // 對頭
    BOOL great = [foo isGreat];
    if (great)

    ◦ 屬性
    ▪ 命名:與去掉“_”前綴的成員變量相同,使用@synthesize將二者聯(lián)系起來。如:
    // abcd.h
    @interface MyClass : NSObject {
    @private
    NSString *_name;
    }
    @property (copy, nonatomic) NSString *name;
    @end
    // abcd.m
    @implementation MyClass
    @synthesize name = _name;
    @end
    ▪ 位置:屬性的聲明緊隨成員變量塊之后,中間空一行,無縮進。如上例所示
    ▪ 嚴把權(quán)限:對不需要外部修改的屬性使用readonly
    ▪ NSString使用copy而非retain
    ▪ CFType使用@dynamic, 禁止使用@synthesize
    ▪ 除非必須,使用nonatomic
    • Cocoa Pattern
    ◦ Delegate Pattern(委托)
    ▪ delegate對象使用assign,禁止使用retain。因為retain會導(dǎo)致循環(huán)索引導(dǎo)致內(nèi)存泄露,
    并且此類型的內(nèi)存泄露無法被Instrument發(fā)現(xiàn),極難調(diào)試
    ▪ 成員變量命名為_delegate,屬性名為delegate
    ◦ Model/View/Controller
    ▪ Model和View分離
    ▪ 不多解釋
    ▪ Controller獨立于View和Controller
    ▪ 不要在與view相關(guān)的類中添加過多的業(yè)務(wù)邏輯代碼,這讓代碼的可重用性很差
    ▪ Controller負責業(yè)務(wù)邏輯代碼,且Controller的代碼與view盡量無關(guān)
    ▪ 使用 @protocal 定義回調(diào)APIs,如果并非所有方法都是必須的,使用 @optional 標示
    • 其他
    ◦ init方法和dealloc方法是是最常用的方法,所以將他們放在類實現(xiàn)的開始位置
    ◦ 使用空格將相同的變量、屬性對齊,使用換行分組