iOS開發代碼編寫規範

軟件編碼要遵循以下原則:
1.遵循開發流程,在設計的指導下進行代碼編寫。
2.代碼的編寫以實現設計的功能和性能爲目標,要求正確完成設計要求的功能,達到設計的性能。
3.程序具有良好的程序結構,提高程序的封裝性好,減低程序的耦合程度。
4.程序可讀性強,易於理解;方便調試和測試,可測試性好。
5.易於使用和維護;良好的修改性、擴充性;可重用性強/移植性好。
6.佔用資源少,以低代價完成任務。
7.在不降低程序的可讀性的情況下,儘量提高代碼的執行效率。


本規範的描述主要以Objective-C語言爲例

一 命名

1.排版約定

(a) 名稱有多個單詞組成時,不要使用特殊字符(標點符號,下劃線,破折號等等)作爲名稱一部分或者分隔符。

(b) 在項目中變量和方法等,都採用“駝峯”命名法。例如:  runTheWordsTogether 

特殊情況是首單詞是廣爲人知的縮寫。如:       TIFFRepresentation(NSImage)

(c) 對於函數名和常量名,首字母大寫並使用和相關聯的類相同的前綴。如:

UIGraphicsGetCurrentContext() //函數

NSCellDisabled //常量

(d) 避免給方法名加下劃線前綴來表示爲私有方法(給實例變量加下劃線前綴來表示私有變量是允許的)。蘋果保留了此約定的使用。

(e) 類名、協議名

類名應當包含能清晰指出該類的功能的名詞。

▪  類名,類別名和協議名的首字母大寫,使用首字母大寫的形式分割單詞

▪  在面向特定應用的代碼中,類名應儘量避免使用前綴,每個類都使用相同的前綴影響可讀性。

▪  協議頭文件裏的協議名不要和類名混淆。如:

LoginDelegate     //協議名,加上Delegate

Login                   //不好。看上去像一個類名。

 (f) 方法名

▪  方法的參數採用“駝峯”命名法

▪  表示操作對象的方法,用動詞開始命名。如:

   - (void)invokeWithTarget:(id)target;

▪  多個參數時,每個參數使用參數標籤

- (void)sendAction:(SEL)aSelector   

                          to:(id)anObject

             forAllCells:(BOOL)flag              正確

  - (void)sendAction:(SEL)aSelector :(id)anObject :(BOOL)flag 錯誤

▪  方法名稱要清晰的表達出方法的功能。如:

- (id)viewWithTag:(NSInteger)aTag;正確

- (id)taggedView:(int)aTag;錯誤

▪  當創建一個比繼承的方法參數更多的方法時,在繼承的方法後邊添

加新的關鍵字。如:

- (id)initWithFrame:(CGRect)frameRect;       //UIView,NSView

- (id)initWithFrame:(CGRect)frameRect 

                     mode:(int)aMode

     numberOfRows:(int)rowsHigh// NSMatrix, a subclass

numberOfColumns:(int)colsWide;   // of NSView

▪  不要使用“and”來連接那些作爲receiver屬性的關鍵字。

- (int)runModalForDirectory:(NSString *)path 

                                       file:(NSString *)name 

                                   types:(NSArray *)fileTypes;     正確;

- (int)runModalForDirectory:(NSString *)path 

                               andFile:(NSString *)name 

                           andTypes:(NSArray *)fileTypes;  錯誤

▪  方法描述了兩個分離的動作,用“and”連接它們;

-(BOOL)opneFile:(NSString *)fulPath

    withApplication:(NSString *)appName

andDeactiovation:(BOOL)flag;

▪  方法名應簡短清晰,但不能因簡短犧牲清晰

insertObject: atIndex:  //好

       insert:at: //不好,不夠清晰;

removeObjectAtIndex://好

removeObject:        //好,因爲它移除參數提及的對象;

       remove:  //不清晰,要移除什麼?

▪  方法名應儘量全拼,即使單詞很長。因爲你寫的縮寫不一定是廣爲人知的,因爲開發者有着各自的文化和語言背景。

destinationSelection://

destSel:   //不

setBackgroundColor;  //好

setBkgdColor:   //不好

當然,少量的縮寫是通常所見和使用了很長曆史的可以排除在外。

例如:alloc,alt, app,calc, dealloc, func, horiz, info, init, int, max, min, msg, nib, pboard, rect, Rep, temp, vert等。

還有計算機工業常用的,

如:ASCII, PDF, XML, HTML, URL, RTF, HTTP, TIFF, JPG, PNG, FIG, 

LZW, ROM, RGB, CMYK, MIDI, FTP等。


▪  API方法名避免有歧義,即能別理解一種以上的意思。如:

sendPort: //不好,是發送端口號還是要返回一個端口號?

▪  如果不需要重寫getter方法時,getter的方法名和變量名應相 同。不允許使用“get”前綴。如: 

- (id) getDelegate; // 禁止

- (id)delegate;// 對頭

重寫getter時可以使用,例如:

 @property(getter=getFontSize)NSInteger fontSize;

▪  訪問器方法根據屬性名稱:

如果屬性爲名詞,格式爲

      - (type)noun;

      - (void)setNoun:(type)aNoun;

如果屬性爲形容詞,格式爲

      - (BOOL)isAdjective;

      - (void)setAdjective:(BOOL)flag;

如果屬性爲動詞,格式爲

      - (BOOL)verbObject;

      - (void)setVerbObject:(BOOL)flag;

不要把動詞用分詞的形式轉成形容詞,例如:

       - (void)setAcceptsGlyphInfo:(BOOL)flag;     正確;

       - (void)setGlyphInfoAccepted:(BOOL)flag;    錯誤

可以使用情態動詞(can, should, will等)明確意思,但不能用do,does。

只有方法間接返回對象和值的時候使用“get”。只有在多個條目需要被返回的時候才能使用如下命名:

 - (void)getLineDash:(float*)pattern 

count:(int*)count phase:(float*)phase;

▪  不同類裏做相同功能的方法應該有相同的方法名稱,即一致性。

▪  前綴有規定的格式:包含兩到三個大寫字母,不能使用下劃線或者 “sub prefixes”.標準如:NS,AB,IB等。在面向多應用的代碼 中,推薦使用前綴。如:GTMSendMessage命名classes,protocols, functions,constants,typedef structures時使用前綴。命名 methods時不要使用前綴;方法是存在於定義它們 的類所創建的 namespace裏。也不要在命名結構體字段時使用前綴。

▪ 如果你是一個大cocoa框架的子類,想絕對確定你的私有方法有一 個不同於其他子類的名字,你可以添加一個自己的前綴給私有方法。 這個前綴儘可能獨一的,基於你的公司或工程的縮寫格式 “XX_method”.

▪  本規則僅針對Objective-C代碼,C++代碼使用C++的編碼習慣。

2. 變量名

▪  變量名應使用容易意會的應用全稱,命名使用“駝峯”表示法。

二 格式化代碼

1. 指針”*”號的位置,“*”前加空格,緊跟參數名

 如: UITextField *mTextField;

2. 每行的長度

每行的長度儘量在100個字符之內,保持美觀大方。

3. 方法的聲明和定義

(a) 在“-”或者“+”和返回值之間留1個空格,方法名和第一個參數間不留。如:

- (void)doSomethingWithString:(NSString *)theString{

      //dosomething

} 

(b) 當參數過長時,每個參數佔用一行,以冒號對齊。如:

- (void)doSomethingWith:(GTMFoo *)theFoo

                                 rect:(NSRect)theRect

                           interval:(float)theInterval {

// dosomething 

(c)  如果方法名比參數名短,每個參數佔用一行,至少縮進4個字符,且爲垂直 對齊(而非使用冒號對齊)。

如:

- (void)short:(GTMFoo *)theFoo

   longKeyword:(NSRect)theRect

   evenLongerKeyword:(float)theInterval {

        // dosomething

}

5.方法的調用

(a) 調用方法沿用聲明方法的習慣。例外:如果給定源文件已經遵從某種習慣,繼續遵從那種習慣。

(b)  所有參數應在同一行中,或者每個參數佔用一行且使用冒號對齊。如:

[myObject doFooWith:arg1 name:arg2 error:arg3];

[myObject doFooWith:arg1

                             name:arg2

                              error:arg3];

(c) 和方法的聲明一樣,如果無法使用冒號對齊時,每個參數一行、縮進4個字符、

垂直對其(而非使用冒號對齊)。

如:


[myObj short:arg1 

longKeyword:arg2

evenLongerKeyword:arg3];

(d) @public 和@private


▪ @public 和@private使用單獨一行,且縮進1個字符

(e) Protocals

▪  類型標示符、代理名稱、尖括號間不留空格。

▪  該規則同樣適用於:類聲明、實例變量和方法聲明。如:

@interfaceMyProtocoledClass : NSObject<NSWindowDelegate> {

 @private

id<MyFancyDelegate> delegate;

}

- (void)setDelegate:(id<MyFancyDelegate>)delegate;

@end


三 Cocoa 和Objective-C特有的規則

1.成員變量

◦成員變量使用@private。如: 

@interfaceMyClass : NSObject {

  @private

 id myInstanceVariable;

}

// public accessors, setter takes ownership

- (id)myInstanceVariable;

- (void)setMyInstanceVariable:(id)theVar;

@end

2. Indentify Designated Initializer 

一個精心設計的初始化方法應當完成以下步驟來確保適當的檢測和誤差傳播。

▪ 通過調用super的指定初始器重新分配self;

▪ 檢測返回值和nil對比,這可以指出一些發生在父類的初始器裏的錯誤。

▪如果初始化當前類時發生了一個錯誤,釋放掉該類對象並返回nil。

如下所示:

- (id)init{

       self = [super init];   //Call a designated initializer here;

       if (self != nil){

              // Initialize object ...

              if (someError){

                     [self release]; 

                     self = nil; 

              }

       }

       return self;

}

1. 初始化

▪ 在初始化方法中,不要將變量初始化爲“0”或“nil”,那是多餘的

▪ 內存中所有的新創建的對象(isa除外)都是0,所以不需要重複初始化爲 “0”或“nil”,避免顯式的調用+new方法

▪ 禁止直接調用NSObject的類方法+new,也不要在子類中重載它。使用alloc和    init方法

4. #import VS #include

▪ 使用#import引入Ojbective-C和Ojbective-C++頭文件,使用,#include引入C和C++頭文件

▪  import根框架(root frameworks),而非各單個文件


▪ 雖然有時我們僅需要框架(如Cocoa 或Foundation)的某幾個頭文件,但引入根文件編譯器會運行的更快。因爲根框架(rootframeworks),一般會預編譯,所以加載會更快。再次強調:使用#import 而非#include 來引入Objective-C框架。如:

#import <Foundation/NSArray.h> // 禁止

#import<Foundation/NSString.h>

...

#import <Foundation/Foundation.h> //正確

5. 創建對象時autorelease 與release 的使用

▪ 一般情況下不用的對象需要release,不建議使用autorelease,在release的時候要嚴格按照內存管理的方法進行。在以下兩種情況下需要使用autorelease :

(a) 方法返回指針類型的對象時需要用到autorelease

- (NSMutableArray *)getBookmarkPreset{

   //return object should autorelease

    NSMutableArray *bookMarkList = [[[NSMutableArray alloc] init] autorelease]; 

    //do something

    return bookMarkList;

}

 

(b) 方法的邏輯判斷比較複雜,有可能提前返回的情況下,建議使用autorelease,雖然這樣會稍微有點慢,但這樣可以阻止因爲提前return 或其他意外情況導致的內存泄露。例如:

MyController* controller = [[[MyController alloc] init] autorelease]; 

// ... 這裏的代碼可能會提前return ...
[controller release];


6. 先release,再retain 

▪  在爲對象賦值時,遵從“先release,再retain” 

▪  在將一個新創建的對象賦給變量時,要先將舊對象release掉,否則會內 存泄露。如:

- (void)setFoo:(GMFoo *)aFoo {

       [foo_ release];

       foo_ = [aFoo retain];

}


▪ dealloc的順序要與變量聲明的順序相同,這有利於review代碼


▪ NSString的屬性的setter使用“copy” ,禁止使用retain,以防止意外的修改了NSString變量的值。如:

  - (void)setFoo:(NSString *)aFoo {

       [foo_ autorelease];

       foo_ = [aFoo copy];

}

 @property (nonatomic, copy) NSString *aString;

7. 對nil 的檢查


▪ 僅在有業務邏輯需求時檢查nil,而非爲了防止崩潰

8. BOOL陷阱

▪  將int值轉換爲BOOL時應特別小心。避免直接和YES比較

▪  Objective-C中,BOOL被定義爲unsigned char,這意味着除了YES(1) 和 NO(0)外它還可以是其他值。禁止將int直接轉換爲BOOL。

▪  常見的錯誤包括:將數組的大小、指針值或位運算符的結果轉換爲 
 BOOL,因爲該BOOL值的結果取決於整型值的最後一 位

▪  將整型值轉換爲BOOL的方法:使用三元運算符返回YES / NO,或使用 位運算符(&&,||,!) 

▪  BOOL、_Bool和bool之間的轉換是安全的,但是BOOL和 Boolean間的轉換不是安全的,所以將Boolean看成整型值。

▪  在Objective-C中,只允許使用BOOL,如: 


// 禁止


- (BOOL)isBold{

 
      return [self fontTraits] &NSFontBoldTrait;        //&得出來的值是int不是Bool,避免出現返回int。

}

- (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) 

9. 屬性

通過使用聲明property和synthesize組合來避免顯式聲明公共實例變 量。如果要顯式聲明一個實例變量,要麼@private或者@protected。如果該 類派生子類,子類需要訪問該類數據,使用@protected。

   ▪ 位置:屬性的聲明緊隨成員變量塊之後,中間空一行,無縮進。

   ▪ 嚴把權限:對不需要外部修改的屬性使用readonly


   ▪ NSString使用copy而非retain。


   ▪ 一般使用nonatomic,除非特殊情況下使用atomic ,例如多線程。

 

四Cocoa Pattern(模式)

1. Delegate Pattern(委託模式)

▪  delegate對象使用assign,禁止使用retain。因爲retain會導致循環索引導 致內存泄露, 
並且此類型的內存泄露無法被Instrument發現,極難調試

2. Model/View/Controller

▪  Model和View分離。

▪  不要在與view相關的類中添加過多的業務邏輯代碼,這讓代碼的可重用性很差 。

▪ Controller負責業務邏輯代碼,且Controller的代碼與view儘量無關


▪ 使用@protocal定義回調APIs,如果並非所有方法都是必須的,可選使用 @optional標示,必選使用 @required標識.

五 其他

1. init方法和dealloc

init和dealloc方法是是最常用的方法,所以將他們放在類實現的開始位置

(a) 使用空格將相同的變量、屬性對齊,使用換行分組

(b) 在設定視圖frame時,會隨屏幕大小變化的視圖使用controller.view.frame 的相對數值。例如:

//MyViewController.m

-(void)viewDidLoad{

       [superviewDidLoad];

       // Do something ...

       UIScrollView *mScrollView = [[UIScrollViewalloc] initWithFrame:  CGRectMake(0, 0, self.view.frame.size.width,  self.view.frame.size.height)];

       [self.viewaddSubview:mScrollView];

}

2. 多view視圖排版

有多個view同時在一個view上時,其之間的間距用變量來設定;

 //MyViewController.m

-(void)viewDidLoad{

       [superviewDidLoad];

       int i = 0;

       UILabel *l1 = [[UILabelalloc ]initWithFrame:CGRectMake(0, i, 100, 30)];

       [self.view addSubview:l1];

       [l1 release];

 

       i+=50;

       UILabel *l2 = [[UILabelalloc ]initWithFrame:CGRectMake(0, i, 100, 30)];

       [self.view addSubview:l2];

       [l2 release];

 

       i+=60;

 

       UILabel *l3 = [[UILabelalloc ]initWithFrame:CGRectMake(0, i, 100, 30)];

       [self.view addSubview:l3];

       [l3 release];

       // Do something ...

}

3. 開發中儘可能使用代碼,xib盡少使用。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章