iOS 編輯代碼規範

# 譯者的話


代碼風格的重要性對於一個團隊和項目來說不言而喻。網上有許多 Objective-C 的代碼風格,但這份簡潔而又最符合蘋果的規範,同時有助於養成良好的代碼習慣,也是我們團隊一直遵循的代碼風格。


原文在[這裏][original_link]

本人才疏學淺,如果有任何翻譯不當歡迎在 [Issues][Issues_link] 中反饋或者直接 [Fork][Fork_link]


[original_link]:https://github.com/NYTimes/objective-c-style-guide


[Issues_link]:https://github.com/VincentSit/NYTimes-Objective-C-Style-Guide-ZH/issues


[Fork_link]:https://github.com/NYTimes/objective-c-style-guide/fork


----


# 紐約時報 移動團隊 Objective-C 規範指南


這份規範指南概括了紐約時報 iOS 團隊的代碼約定。


## 介紹


關於這個編程語言的所有規範,如果這裏沒有寫到,那就在蘋果的文檔裏: 


* [Objective-C 編程語言][Introduction_1]

* [Cocoa 基本原理指南][Introduction_2]

* [Cocoa 編碼指南][Introduction_3]

* [iOS 應用編程指南][Introduction_4]


[Introduction_1]:http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/ObjectiveC/Introduction/introObjectiveC.html


[Introduction_2]:https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/CocoaFundamentals/Introduction/Introduction.html


[Introduction_3]:https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/CodingGuidelines/CodingGuidelines.html


[Introduction_4]:http://developer.apple.com/library/ios/#documentation/iphone/conceptual/iphoneosprogrammingguide/Introduction/Introduction.html



## 目錄


* [點語法](#點語法)

* [間距](#間距)

* [條件判斷](#條件判斷)

* [三目運算符](#三目運算符)

* [錯誤處理](#錯誤處理)

* [方法](#方法)

* [變量](#變量)

* [命名](#命名)

* [註釋](#註釋)

* [Init Dealloc](#init--dealloc)

* [字面量](#字面量)

* [CGRect 函數](#CGRect-函數)

* [常量](#常量)

* [枚舉類型](#枚舉類型)

* [位掩碼](#位掩碼)

* [私有屬性](#私有屬性)

* [圖片命名](#圖片命名)

* [布爾](#布爾)

* [單例](#單例)

* [導入](#導入)

* [Xcode 工程](#Xcode-工程)


## 點語法


應該 **始終** 使用點語法來訪問或者修改屬性,訪問其他實例時首選括號。


**推薦:**

```objc

view.backgroundColor = [UIColor orangeColor];

[UIApplication sharedApplication].delegate;

```


**反對:**

```objc

[view setBackgroundColor:[UIColor orangeColor]];

UIApplication.sharedApplication.delegate;

```


## 間距


* 一個縮進使用 4 個空格,永遠不要使用製表符(tab)縮進。請確保在 Xcode 中設置了此偏好。

* 方法的大括號和其他的大括號(`if`/`else`/`switch`/`while` 等等)始終和聲明在同一行開始,在新的一行結束。


**推薦:**

```objc

if (user.isHappy) {

// Do something

}

else {

// Do something else

}

```

* 方法之間應該正好空一行,這有助於視覺清晰度和代碼組織性。在方法中的功能塊之間應該使用空白分開,但往往可能應該創建一個新的方法。

* `@synthesize` `@dynamic` 在實現中每個都應該佔一個新行。



## 條件判斷


條件判斷主體部分應該始終使用大括號括住來防止[出錯][Condiationals_1],即使它可以不用大括號(例如它只需要一行)。這些錯誤包括添加第二行(代碼)並希望它是 if 語句的一部分時。還有另外一種[更危險的][Condiationals_2],當 if 語句裏面的一行被註釋掉,下一行就會在不經意間成爲了這個 if 語句的一部分。此外,這種風格也更符合所有其他的條件判斷,因此也更容易檢查。


**推薦:**

```objc

if (!error) {

    return success;

}

```


**反對:**

```objc

if (!error)

    return success;

```



```objc

if (!error) return success;

```



[Condiationals_1]:(https://github.com/NYTimes/objective-c-style-guide/issues/26#issuecomment-22074256)

[Condiationals_2]:http://programmers.stackexchange.com/a/16530


### 三目運算符


三目運算符,? ,只有當它可以增加代碼清晰度或整潔時才使用。單一的條件都應該優先考慮使用。多條件時通常使用 if 語句會更易懂,或者重構爲實例變量。


**推薦:**

```objc

result = a > b ? x : y;

```


**反對:**

```objc

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

```


## 錯誤處理


當引用一個返回錯誤參數(error parameter)的方法時,應該針對返回值,而非錯誤變量。


**推薦:**

```objc

NSError *error;

if (![self trySomethingWithError:&error]) {

    // 處理錯誤

}

```


**反對:**

```objc

NSError *error;

[self trySomethingWithError:&error];

if (error) {

    // 處理錯誤

}

```

一些蘋果的 API 在成功的情況下會寫一些垃圾值給錯誤參數(如果非空),所以針對錯誤變量可能會造成虛假結果(以及接下來的崩潰)。


## 方法


在方法簽名中,在 -/+ 符號後應該有一個空格。方法片段之間也應該有一個空格。


**推薦:**

```objc

- (void)setExampleText:(NSString *)text image:(UIImage *)image;

```


## 變量


變量名應該儘可能命名爲描述性的。除了 `for()` 循環外,其他情況都應該避免使用單字母的變量名。

星號表示指針屬於變量,例如:`NSString *text` 不要寫成 `NSString* text` 或者 `NSString * text` ,常量除外。

儘量定義屬性來代替直接使用實例變量。除了初始化方法(`init` `initWithCoder:`,等), `dealloc` 方法和自定義的 setters getters 內部,應避免直接訪問實例變量。更多有關在初始化方法和 dealloc 方法中使用訪問器方法的信息,參見[這裏][Variables_1]



**推薦:**


```objc

@interface NYTSection: NSObject


@property (nonatomic) NSString *headline;


@end

```


**反對:**


```objc

@interface NYTSection : NSObject {

    NSString *headline;

}

```


[Variables_1]:https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/MemoryMgmt/Articles/mmPractical.html#//apple_ref/doc/uid/TP40004447-SW6


#### 變量限定符


當涉及到[ ARC 中被引入][Variable_Qualifiers_1]變量限定符時,

限定符 (`__strong`, `__weak`, `__unsafe_unretained`, `__autoreleasing`) 應該位於星號和變量名之間,如:`NSString * __weak text`


[Variable_Qualifiers_1]:(https://developer.apple.com/library/ios/releasenotes/objectivec/rn-transitioningtoarc/Introduction/Introduction.html#//apple_ref/doc/uid/TP40011226-CH1-SW4)


## 命名


儘可能遵守蘋果的命名約定,尤其那些涉及到[內存管理規則][Naming_1],([NARC][Naming_2])的。


長的和描述性的方法名和變量名都不錯。


**推薦:**


```objc

UIButton *settingsButton;

```


**反對:**


```objc

UIButton *setBut;

```

類名和常量應該始終使用三個字母的前綴(例如 `NYT`),但 Core Data 實體名稱可以省略。爲了代碼清晰,常量應該使用相關類的名字作爲前綴並使用駝峯命名法。


**推薦:**


```objc

static const NSTimeInterval NYTArticleViewControllerNavigationFadeAnimationDuration = 0.3;

```


**反對:**


```objc

static const NSTimeInterval fadetime = 1.7;

```


屬性和局部變量應該使用駝峯命名法並且首字母小寫。


爲了保持一致,實例變量應該使用駝峯命名法命名,並且首字母小寫,以下劃線爲前綴。這與 LLVM 自動合成的實例變量相一致。

**如果 LLVM 可以自動合成變量,那就讓它自動合成。**


**推薦:**


```objc

@synthesize descriptiveVariableName = _descriptiveVariableName;

```


**反對:**


```objc

id varnm;

```


[Naming_1]:https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/MemoryMgmt/Articles/MemoryMgmt.html


[Naming_2]:http://stackoverflow.com/a/2865194/340508


## 註釋


當需要的時候,註釋應該被用來解釋 **爲什麼** 特定代碼做了某些事情。所使用的任何註釋必須保持最新否則就刪除掉。


通常應該避免一大塊註釋,代碼就應該儘量作爲自身的文檔,只需要隔幾行寫幾句說明。這並不適用於那些用來生成文檔的註釋。



## init dealloc


`dealloc` 方法應該放在實現文件的最上面,並且剛好在 `@synthesize` `@dynamic` 語句的後面。在任何類中,`init` 都應該直接放在 `dealloc` 方法的下面。


`init` 方法的結構應該像這樣:


```objc

- (instancetype)init {

    self = [super init]; // 或者調用指定的初始化方法

    if (self) {

        // Custom initialization

    }


    return self;

}

```


## 字面量


每當創建 `NSString` `NSDictionary` `NSArray`,和 `NSNumber` 類的不可變實例時,都應該使用字面量。要注意 `nil` 值不能傳給 `NSArray` `NSDictionary` 字面量,這樣做會導致崩潰。


**推薦:**


```objc

NSArray *names = @[@"Brian", @"Matt", @"Chris", @"Alex", @"Steve", @"Paul"];

NSDictionary *productManagers = @{@"iPhone" : @"Kate", @"iPad" : @"Kamal", @"Mobile Web" : @"Bill"};

NSNumber *shouldUseLiterals = @YES;

NSNumber *buildingZIPCode = @10018;

```


**反對:**


```objc

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 *buildingZIPCode = [NSNumber numberWithInteger:10018];

```


## CGRect 函數


當訪問一個 `CGRect` `x` `y` `width` `height` 時,應該使用[`CGGeometry` 函數][CGRect-Functions_1]代替直接訪問結構體成員。蘋果的 `CGGeometry` 參考中說到:


> 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.


**推薦:**


```objc

CGRect frame = self.view.frame;


CGFloat x = CGRectGetMinX(frame);

CGFloat y = CGRectGetMinY(frame);

CGFloat width = CGRectGetWidth(frame);

CGFloat height = CGRectGetHeight(frame);

```


**反對:**


```objc

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-Functions_1]:http://developer.apple.com/library/ios/#documentation/graphicsimaging/reference/CGGeometry/Reference/reference.html


## 常量


常量首選內聯字符串字面量或數字,因爲常量可以輕易重用並且可以快速改變而不需要查找和替換。常量應該聲明爲 `static` 常量而不是 `#define` ,除非非常明確地要當做宏來使用。


**推薦:**


```objc

static NSString * const NYTAboutViewControllerCompanyName = @"The New York Times Company";


static const CGFloat NYTImageThumbnailHeight = 50.0;

```


**反對:**


```objc

#define CompanyName @"The New York Times Company"


#define thumbnailHeight 2

```


## 枚舉類型


當使用 `enum` 時,建議使用新的基礎類型規範,因爲它具有更強的類型檢查和代碼補全功能。現在 SDK 包含了一個宏來鼓勵使用使用新的基礎類型 - `NS_ENUM()`


**推薦:**


```objc

typedef NS_ENUM(NSInteger, NYTAdRequestState) {

    NYTAdRequestStateInactive,

    NYTAdRequestStateLoading

};

```


## 位掩碼


當用到位掩碼時,使用 `NS_OPTIONS` 宏。


**舉例:**


```objc

typedef NS_OPTIONS(NSUInteger, NYTAdCategory) {

NYTAdCategoryAutos      = 1 << 0,

NYTAdCategoryJobs       = 1 << 1,

NYTAdCategoryRealState  = 1 << 2,

NYTAdCategoryTechnology = 1 << 3

};

```



## 私有屬性


私有屬性應該聲明在類實現文件的延展(匿名的類目)中。有名字的類目(例如 `NYTPrivate` `private`)永遠都不應該使用,除非要擴展其他類。


**推薦:**


```objc

@interface NYTAdvertisement ()


@property (nonatomic, strong) GADBannerView *googleAdView;

@property (nonatomic, strong) ADBannerView *iAdView;

@property (nonatomic, strong) UIWebView *adXWebView;


@end

```


## 圖片命名


圖片名稱應該被統一命名以保持組織的完整。它們應該被命名爲一個說明它們用途的駝峯式字符串,其次是自定義類或屬性的無前綴名字(如果有的話),然後進一步說明顏色 / 展示位置,最後是它們的狀態。


**推薦:**


* `RefreshBarButtonItem` / `RefreshBarButtonItem@2x` `RefreshBarButtonItemSelected` / `RefreshBarButtonItemSelected@2x`

* `ArticleNavigationBarWhite` / `ArticleNavigationBarWhite@2x` `ArticleNavigationBarBlackSelected` / `ArticleNavigationBarBlackSelected@2x`.


圖片目錄中被用於類似目的的圖片應歸入各自的組中。



## 布爾


因爲 `nil` 解析爲 `NO`,所以沒有必要在條件中與它進行比較。永遠不要直接和 `YES` 進行比較,因爲 `YES` 被定義爲 1,而 `BOOL` 可以多達 8 位。


這使得整個文件有更多的一致性和更大的視覺清晰度。


**推薦:**


```objc

if (!someObject) {

}

```


**反對:**


```objc

if (someObject == nil) {

}

```


-----


**對於 `BOOL` 來說, 這有兩種用法:**


```objc

if (isAwesome)

if (![someObject boolValue])

```


**反對:**


```objc

if ([someObject boolValue] == NO)

if (isAwesome == YES) // 永遠別這麼做

```


-----


如果一個 `BOOL` 屬性名稱是一個形容詞,屬性可以省略 “is” 前綴,但爲 get 訪問器指定一個慣用的名字,例如:


```objc

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

```


內容和例子來自 [Cocoa 命名指南][Booleans_1]


[Booleans_1]:https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/CodingGuidelines/Articles/NamingIvarsAndTypes.html#//apple_ref/doc/uid/20001284-BAJGIIJE



## 單例


單例對象應該使用線程安全的模式創建共享的實例。


```objc

+ (instancetype)sharedInstance {

   static id sharedInstance = nil;


   static dispatch_once_t onceToken;

   dispatch_once(&onceToken, ^{

      sharedInstance = [[self alloc] init];

   });


   return sharedInstance;

}

```

這將會預防[有時可能產生的許多崩潰][Singletons_1]


[Singletons_1]:http://cocoasamurai.blogspot.com/2011/04/singletons-your-doing-them-wrong.html


## 導入   


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

注:對於模塊使用 [@import][Import_2] 語法。   


```objc   

// Frameworks

@import QuartzCore;


// Models

#import "NYTUser.h"


// Views

#import "NYTButton.h"

#import "NYTUserView.h"

```   



[Import_1]: http://ashfurrow.com/blog/structuring-modern-objective-c

[Import_2]: http://clang.llvm.org/docs/Modules.html#using-modules


## Xcode 工程


爲了避免文件雜亂,物理文件應該保持和 Xcode 項目文件同步。Xcode 創建的任何組(group)都必須在文件系統有相應的映射。爲了更清晰,代碼不僅應該按照類型進行分組,也可以根據功能進行分組。



如果可以的話,儘可能一直打開 target Build Settings "Treat Warnings as Errors" 以及一些[額外的警告][Xcode-project_1]。如果你需要忽略指定的警告,使用 [Clang 的編譯特性][Xcode-project_2]



[Xcode-project_1]:http://boredzo.org/blog/archives/2009-11-07/warnings


[Xcode-project_2]:http://clang.llvm.org/docs/UsersManual.html#controlling-diagnostics-via-pragmas



# 其他 Objective-C 風格指南


如果感覺我們的不太符合你的口味,可以看看下面的風格指南:


* [Google](http://google-styleguide.googlecode.com/svn/trunk/objcguide.xml)

* [GitHub](https://github.com/github/objective-c-conventions)

* [Adium](https://trac.adium.im/wiki/CodingStyle)

* [Sam Soffes](https://gist.github.com/soffes/812796)

* [CocoaDevCentral](http://cocoadevcentral.com/articles/000082.php)

* [Luke Redpath](http://lukeredpath.co.uk/blog/my-objective-c-style-guide.html)

* [Marcus Zarra](http://www.cimgf.com/zds-code-style-guide/)



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