天天看點

IOS團隊程式設計規範

本文講的是<b>IOS團隊程式設計規範,</b>需求是暫時的,隻有變化才是永恒的,面向變化程式設計,而不是面向需求程式設計。

不要過分追求技巧,降低程式的可讀性。

簡潔的代碼可以讓bug無處藏身。要寫出明顯沒有bug的代碼,而不是沒有明顯bug的代碼。

先把眼前的問題解決掉,解決好,再考慮将來的擴充問題。

一、命名規範

1、統一要求

含義清楚,盡量做到不需要注釋也能了解其作用,若做不到,就加注釋,使用全稱,不使用縮寫。

2、類名

大駝峰式命名:每個單詞的首字母都采用大寫字母

==例:== MFHomePageViewController

3、私有變量

私有變量放在 .m 檔案中聲明

以 _ 開頭,第一個單詞首字母小寫,後面的單詞的首字母全部大寫。

==例:== NSString *_somePrivateVariable

4、property變量

小駝峰式命名:第一個單詞以小寫字母開始,後面的單詞的首字母全部大寫

屬性的關鍵字推薦按照 原子性,讀寫,記憶體管理的順序排列。

Block、NSString屬性應該使用copy關鍵字

禁止使用synthesize關鍵詞

==例:==

typedef void (^ErrorCodeBlock) (id errorCode,NSString *message); 

@property (nonatomic, readwrite, strong) UIView *headerView; //注釋 

@property (nonatomic, readwrite, copy) ErrorCodeBlock errorBlock; //将block拷貝到堆中 

@property (nonatomic, readwrite, copy) NSString *userName;  

5、宏和常量命名

對于宏定義的常量

#define 預處理定義的常量全部大寫,單詞間用 _ 分隔

宏定義中如果包含表達式或變量,表達式或變量必須用小括号括起來。

對于類型常量

對于局限于某編譯單元(實作檔案)的常量,以字元k開頭,例如kAnimationDuration,且需要以static const修飾

對于定義于類頭檔案的常量,外部可見,則以定義該常量所在類的類名開頭,例如EOCViewClassAnimationDuration, 仿照蘋果風格,在頭檔案中進行extern聲明,在實作檔案中定義其值

//宏定義的常量 

#define ANIMATION_DURATION    0.3 

#define MY_MIN(A, B)  ((A)&gt;(B)?(B):(A)) 

//局部類型常量 

static const NSTimeInterval kAnimationDuration = 0.3; 

//外部可見類型常量 

//EOCViewClass.h 

extern const NSTimeInterval EOCViewClassAnimationDuration; 

extern NSString *const EOCViewClassStringConstant;  //字元串類型 

//EOCViewClass.m 

const NSTimeInterval EOCViewClassAnimationDuration = 0.3; 

NSString *const EOCViewClassStringConstant = @"EOCStringConstant";  

6、Enum

Enum類型的命名與類的命名規則一緻

Enum中枚舉内容的命名需要以該Enum類型名稱開頭

NS_ENUM定義通用枚舉,NS_OPTIONS定義位移枚舉

typedef NS_ENUM(NSInteger, UIViewAnimationTransition) { 

    UIViewAnimationTransitionNone, 

    UIViewAnimationTransitionFlipFromLeft, 

    UIViewAnimationTransitionFlipFromRight, 

    UIViewAnimationTransitionCurlUp, 

    UIViewAnimationTransitionCurlDown, 

}; 

typedef NS_OPTIONS(NSUInteger, UIControlState) { 

    UIControlStateNormal       = 0, 

    UIControlStateHighlighted  = 1  

7、Delegate

用delegate做字尾,如

用optional修飾可以不實作的方法,用required修飾必須實作的方法

當你的委托的方法過多, 可以拆分資料部分和其他邏輯部分, 資料部分用dataSource做字尾. 如

使用did和will通知Delegate已經發生的變化或将要發生的變化。

類的執行個體必須為回調方法的參數之一

回調方法的參數隻有類自己的情況,方法名要符合實際含義

回調方法存在兩個以上參數的情況,以類的名字開頭,以表明此方法是屬于哪個類的

@protocol UITableViewDataSource    

@required   

//回調方法存在兩個以上參數 

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section;   

@optional 

//回調方法的參數隻有類自己 

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView;              // Default is 1 if not implemented 

@protocol UITableViewDelegate    

//使用`did`和`will`通知`Delegate` 

- (nullable NSIndexPath *)tableView:(UITableView *)tableView willSelectRowAtIndexPath:(NSIndexPath *)indexPath; 

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath;  

8、方法

方法名用小駝峰式命名

方法名不要使用new作為字首

不要使用and來連接配接屬性參數,如果方法描述兩種獨立的行為,使用and來串接它們。

方法實作時,如果參數過長,則令每個參數占用一行,以冒号對齊。

一般方法不使用字首命名,私有方法可以使用統一的字首來分組和辨識

方法名要與對應的參數名保持高度一緻

表示對象行為的方法、執行性的方法應該以動詞開頭

傳回性的方法應該以傳回的内容開頭,但之前不要加get,除非是間接傳回一個或多個值。

可以使用情态動詞(動詞前面can、should、will等)進一步說明屬性意思,但不要使用do或does,因為這些助動詞沒什麼實際意義。也不要在動詞前使用副詞或形容詞修飾

//不要使用 and 來連接配接屬性參數 

- (int)runModalForDirectory:(NSString *)path file:(NSString *)name types:(NSArray *)fileTypes;    //推薦 

- (int)runModalForDirectory:(NSString *)path andFile:(NSString *)name andTypes:(NSArray *)fileTypes;    //反對  

//表示對象行為的方法、執行性的方法 

- (void)insertModel:(id)model atIndex:(NSUInteger)atIndex; 

- (void)selectTabViewItem:(NSTableViewItem *)tableViewItem  

//傳回性的方法 

- (instancetype)arrayWithArray:(NSArray *)array;  

//參數過長的情況 

- (void)longMethodWith:(NSString *)theFoo  

                  rect:(CGRect)theRect  

              interval:(CGFloat)theInterval  

{  

   //Implementation  

}    

//不要加get  

- (NSSize) cellSize;  //推薦  

- (NSSize) getCellSize;  //反對   

//使用情态動詞,不要使用do或does 

- (BOOL)canHide;  //推薦  

- (BOOL)shouldCloseDocument;  //推薦  

- (BOOL)doesAcceptGlyphInfo;  //反對  

二、代碼注釋規範

優秀的代碼大部分是可以自描述的,我們完全可以用代碼本身來表達它到底在幹什麼,而不需要注釋的輔助。

但并不是說一定不能寫注釋,有以下三種情況比較适合寫注釋:

公共接口(注釋要告訴閱讀代碼的人,目前類能實作什麼功能)。

涉及到比較深層專業知識的代碼(注釋要展現出實作原理和思想)。

容易産生歧義的代碼(但是嚴格來說,容易讓人産生歧義的代碼是不允許存在的)。

除了上述這三種情況,如果别人隻能依靠注釋才能讀懂你的代碼的時候,就要反思代碼出現了什麼問題。

最後,對于注釋的内容,相對于“做了什麼”,更應該說明“為什麼這麼做”。

1、import注釋

如果有一個以上的import語句,就對這些語句進行分組,每個分組的注釋是可選的。

// Frameworks  

#import ;  

// Models  

#import "NYTUser.h"  

// Views  

#import "NYTButton.h"  

#import "NYTUserView.h" 

2、屬性注釋

寫在屬性之後,用兩個空格隔開

3、方法聲明注釋:

一個函數(方法)必須有一個字元串文檔來解釋,除非它:

非公開,私有函數。

很短。

顯而易見。

而其餘的,包括公開接口,重要的方法,分類,以及協定,都應該伴随文檔(注釋):

以/開始

第二行是總結性的語句

第三行永遠是空行

在與第二行開頭對齊的位置寫剩下的注釋。

建議這樣寫:

/This comment serves to demonstrate the format of a doc string. 

Note that the summary line is always at most one line long, and after the opening block comment, 

and each line of text is preceded by a single space. 

*/  

方法的注釋使用Xcode自帶注釋快捷鍵:Commond+option+/

/** 

@param tableView 

@param section 

&lt;a href='http://www.jobbole.com/members/wx1409399284'&gt;@return&lt;/a&gt; 

*/ 

- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section 

    //... 

}  

4、代碼塊注釋

單行的用//+空格開頭,多行的采用/* */注釋

5、TODO

使用//TODO:說明 标記一些未完成的或完成的不盡如人意的地方

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 

    //TODO:增加初始化 

    return YES; 

三、代碼格式化規範

1、指針*位置

定義一個對象時,指針*靠近變量

==例:== NSString *userName;

2、方法的聲明和定義

在 - 、+和 傳回值之間留一個空格,方法名和第一個參數之間不留白格

- (void)insertSubview:(UIView *)view atIndex:(NSInteger)index; 

3、代碼縮進

不要在工程裡使用 Tab 鍵,使用空格來進行縮進。在 Xcode &gt; Preferences &gt; Text Editing 将 Tab 和自動縮進都設定為 4 個空格

Method與Method之間空一行

一進制運算符與變量之間沒有空格、二進制運算符與變量之間必須有空格

!bValue 

fLength = fWidth * 2;   

- (void)sampleMethod1;   

- (void)sampleMethod2;  

4、對method進行分組

使用#pragma mark -對method進行分組

#pragma mark - Life Cycle Methods 

- (instancetype)init 

- (void)dealloc   

- (void)viewWillAppear:(BOOL)animated 

- (void)viewDidAppear:(BOOL)animated 

- (void)viewWillDisappear:(BOOL)animated 

- (void)viewDidDisappear:(BOOL)animated   

#pragma mark - Override Methods 

#pragma mark - Intial Methods   

#pragma mark - Network Methods   

#pragma mark - Target Methods  

#pragma mark - Public Methods   

#pragma mark - Private Methods   

#pragma mark - UITableViewDataSource   

#pragma mark - UITableViewDelegate    

#pragma mark - Lazy Loads  

#pragma mark - NSCopying    

#pragma mark - NSObject  Methods  

5、大括号寫法

對于類的method:左括号另起一行寫(遵循蘋果官方文檔)

對于其他使用場景(if,for,while,switch等): 左括号跟在第一行後邊

- (void)sampleMethod 

    BOOL someCondition = YES; 

    if(someCondition) { 

        // do something here 

    } 

6、property變量

四、編碼規範

1、if語句

①、須列出所有分支(窮舉所有的情況),而且每個分支都須給出明确的結果。

==推薦這樣寫:==

var hintStr; 

if (count &lt; 3) { 

  hintStr = "Good"; 

} else { 

  hintStr = ""; 

==不推薦這樣寫:==

 hintStr = "Good"; 

②、不要使用過多的分支,要善于使用return來提前傳回錯誤的情況,把最正确的情況放到最後傳回。

if (!user.UserName) return NO; 

if (!user.Password) return NO; 

if (!user.Email) return NO;   

return YES;  

BOOL isValid = NO; 

if (user.UserName) 

    if (user.Password) 

    { 

        if (user.Email) isValid = YES; 

return isValid;  

③、條件過多,過長的時候應該換行。條件表達式如果很長,則需要将他們提取出來賦給一個BOOL值,或者抽取出一個方法

if (condition1 &amp;&amp; 

    condition2 &amp;&amp; 

    condition3 &amp;&amp; 

    condition4) { 

  // Do something 

BOOL finalCondition = condition1 &amp;&amp; condition2 &amp;&amp; condition3 &amp;&amp; condition4 

if (finalCondition) { 

if ([self canDelete]){ 

- (BOOL)canDelete 

    BOOL finalCondition1 = condition1 &amp;&amp; condition2 

    BOOL finalCondition2 =  condition3 &amp;&amp; condition4 

    return condition1 &amp;&amp; condition2; 

if (condition1 &amp;&amp; condition2 &amp;&amp; condition3 &amp;&amp; condition4) { 

④、條件語句的判斷應該是變量在右,常量在左。

==推薦:==

if (6 == count) { 

}   

if (nil == object) { 

if (!object) { 

==不推薦:==

if (count == 6) { 

if (object == nil) { 

if (object == nil)容易誤寫成指派語句,if (!object)寫法很簡潔

⑤、每個分支的實作代碼都須被大括号包圍

if (!error) { 

  return success; 

if (!error) 

  return success;  

可以如下這樣寫:

if (!error) return success; 

2、for語句

①、不可在for循環内修改循環變量,防止for循環失去控制。

for (int index = 0; index &lt; 10; index++){ 

   ... 

   logicToChange(index) 

②、避免使用continue和break。

continue和break所描述的是“什麼時候不做什麼”,是以為了讀懂二者所在的代碼,我們需要在頭腦裡将他們取反。

其實最好不要讓這兩個東西出現,因為我們的代碼隻要展現出“什麼時候做什麼”就好了,而且通過适當的方法,是可以将這兩個東西消滅掉的:

如果出現了continue,隻需要把continue的條件取反即可

var filteredProducts = Array() 

for level in products { 

    if level.hasPrefix("bad") { 

        continue 

    filteredProducts.append(level) 

我們可以看到,通過判斷字元串裡是否含有“bad”這個prefix來過濾掉一些值。其實我們是可以通過取反,來避免使用continue的:

for level in products {  

    if !level.hasPrefix("bad") {  

      filteredProducts.append(level)  

    }  

消除while裡的break:将break的條件取反,并合并到主循環裡

在while裡的break其實就相當于“不存在”,既然是不存在的東西就完全可以在最開始的條件語句中将其排除。

while裡的break:

while (condition1) { 

  ... 

  if (condition2) { 

    break; 

  } 

取反并合并到主條件:

while (condition1 &amp;&amp; !condition2) { 

... 

在有傳回值的方法裡消除break:将break轉換為return立即傳回

有人喜歡這樣做:在有傳回值的方法裡break之後,再傳回某個值。其實完全可以在break的那一行直接傳回。

func hasBadProductIn(products: Array) -&gt; Bool { 

    var result = false     

    for level in products { 

        if level.hasPrefix("bad") { 

            result = true 

            break 

        } 

   return result 

遇到錯誤條件直接傳回:

            return true 

   return false 

這樣寫的話不用特意聲明一個變量來特意儲存需要傳回的值,看起來非常簡潔,可讀性高。

3、Switch語句

①、每個分支都必須用大括号括起來

推薦這樣寫:

switch (integer) {    

  case 1:  {  

    // ...    

   }  

    break;    

  case 2: {   

    break;  

  }    

  default:{  

  }  

②、使用枚舉類型時,不能有default分支, 除了使用枚舉類型以外,都必須有default分支

RWTLeftMenuTopItemType menuType = RWTLeftMenuTopItemMain;    

switch (menuType) {    

  case RWTLeftMenuTopItemMain: {  

  case RWTLeftMenuTopItemShows: {  

  case RWTLeftMenuTopItemSchedule: {  

在Switch語句使用枚舉類型的時候,如果使用了default分支,在将來就無法通過編譯器來檢查新增的枚舉類型了。

4、函數

①、一個函數隻做一件事(單一原則)

每個函數的職責都應該劃分的很明确(就像類一樣)。

dataConfiguration()  

viewConfiguration()  

void dataConfiguration() 

  viewConfiguration() 

②、對于有傳回值的函數(方法),每一個分支都必須有傳回值

int function() 

    if(condition1){ 

        return count1 

    }else if(condition2){ 

        return count2 

    }else{ 

       return defaultCount 

③、對輸入參數的正确性和有效性進行檢查,參數錯誤立即傳回

void function(param1,param2) 

      if(param1 is unavailable){ 

           return; 

      }  

      if(param2 is unavailable){ 

      } 

     //Do some right thing 

④、如果在不同的函數内部有相同的功能,應該把相同的功能抽取出來單獨作為另一個函數

原來的調用:

void logic() {  

  a();  

  b();  

  if (logic1 condition) {  

    c();  

  } else {  

    d();  

将a,b函數抽取出來作為單獨的函數

void basicConfig() { 

  a(); 

  b(); 

void logic1() { 

  basicConfig(); 

  c(); 

void logic2() { 

  d(); 

⑤、将函數内部比較複雜的邏輯提取出來作為單獨的函數

一個函數内的不清晰(邏輯判斷比較多,行數較多)的那片代碼,往往可以被提取出去,構成一個新的函數,然後在原來的地方調用它這樣你就可以使用有意義的函數名來代替注釋,增加程式的可讀性。

舉一個發送郵件的例子:

openEmailSite(); 

login();     

writeTitle(title); 

writeContent(content); 

writeReceiver(receiver); 

addAttachment(attachment); 

send();  

中間的部分稍微長一些,我們可以将它們提取出來:

void writeEmail(title, content,receiver,attachment) 

  writeTitle(title); 

  writeContent(content); 

  writeReceiver(receiver); 

  addAttachment(attachment); 

然後再看一下原來的代碼:

login(); 

writeEmail(title, content,receiver,attachment) 

send();   

本文作者:佚名

來源:51CTO

原文标題:IOS團隊程式設計規範

繼續閱讀