Objective-C 編碼規範

1.命名git

基本原則

  • 仿照 Cocoa 風格來,使用長命名風格
  • 變量命名推薦的命名語素順序是:最開頭是命名空間簡寫,而後越重要、區別度越大的語素越要往前放。經典的結構是:做用範圍+限定修飾+類型。例:
1
2
3
4
5
6
extern ushort APIDefaultPageSize; // 還行,能明白意思了
extern ushort APIDefaultFetchPageSize; // 加上些限定更好一些
extern ushort APIFetchPageSizeDefault; // 再好些,把重要的往前放

YHToolbarComment // 不推薦
YHCommentToolbar // OK,把類型(toolbar)置後

命名空間

  • 類名、protocols、C 函數、常量、結構體和枚舉應帶有命名空間前綴;
  • 類方法不要帶前綴,結構體字段也不要帶前綴

視圖命名

爲了舉例,咱們假定有 UserTagCategory 這幾種 model 類型。github

對象展現通常分列表和單個詳情,其 view controller 分別使用 ModelListController 和 ModelDetailController,推薦的語素順序是:Model名 + 限定與修飾 + ListController|DetailController。舉例說明:json

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// OK
TagUserUsedListController
TagInCategoryListController
CategoryDetailController

// 不推薦,列表統一使用 ListController,不指明是 table view 仍是 collection view
UserFollowerTableViewController

// OK
UserFollowerListController

// 不推薦
UserLikedTagListController

// OK,把顯示的對象放在第一位
TagUserLikedListController

// 糟糕,若是是 view controller,必須以 Controller 或 Displayer 結尾
TagListView

常常爲了便於多個界面複用,咱們會把 model 的顯示統一在一個 view controller 中,在其餘界面嵌入這個 view controller。咱們把這類專門管理顯示的 view controller 叫作 displayer。如:多線程

1
2
UserListDisplayer
TagListDisplayer

UIView 級別的組件不要以 Controller 或 Displayer 結尾,若是起到管理做用可使用 control 結尾。函數

動機工具

把 model 名放在首位(如 TagUserLikedListController 而不是 UserLikedTagListController)的主要考量是便於搜索。由於 Xcode 不支持亂序搜索,關鍵詞只能從前日後纔會有結果。性能

若是限定詞在前,由於不一樣人理解差別,本身也會遺忘,這個限定詞常常是輸入不能的,只能搜 TagList 再從列表中查找,等於第一位的查找語素就廢掉了。當 model 類型在第一位時,基本上熟悉這個項目的人都清楚要查找的視圖顯示的是什麼類型,第一位正確了,後面添加/修改限定就很方便了。ui

另外一個便利的場景是參考以前界面實現另外一個界面時,查找的大都是相同類型的界面,如實現 UserFollowerListController 參考 UserFollowingListController;而相同限定的場景比較少見,像 UserLikedTagListController 參考 UserLikedCategoryListController 的可能性就較少。編碼

PS: 務必常用 Xcode 的 Open Quickly(默認快捷鍵 Command+Shift+O)spa

方法名

  • 以 alloccopyinitmutableCopynew 開頭的方法要注意,它們會改變ARC的行爲。[^1]
  • 以 getset 開頭的方法有特殊的意義,不要隨意定義。
    1. set 是屬性默認的設置方法,若是函數不是爲了設置類成員,則不要用 set 開頭,可用 setup 替代。
    2. get 和屬性方法無關,但在 Cocoa 中,其標準行爲是經過引用傳值,而不是直接返回結果的。欲獲取變量,直接以變量名爲名,如:userInfomation,而不是 getUserInfomation

例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
Objective-C
// OK
- (NSString *)name;

// 糟糕,應用上面的寫法
- (NSString *)getName;

// OK,但極少使用
- (void)getName:(NSString **)buffer range:(NSRange)inRange;


// OK
- (NSSize)cellSize;

// 糟糕,應用上面的寫法
- (NSSize)calcCellSize;


// 對 controller 作通常設置,OK
- (void)setupController;

// 列出具體設置的內容,更好
- (void)setupControllerObservers;

// 糟糕,set 專用於設置屬性
- (void)setController;
1
2
3
4
5
6
7
Objective-C
// 來自官方文檔
insertObject:atIndex: // OK
insert:at: // 不清晰,插入了什麼?at 具體指哪裏?
removeObjectAtIndex: // OK
removeObject: // OK
remove: // 糟糕,什麼被移除了?

協議名

好的協議名應能馬上讓人分辨出這不是一個類名,除了以經常使用的 delegate、dateSource 作結尾外,還可使用 …ing 這種形式,如:NSCodingNSCopyingNSLocking

通知命名

基本命名格式是:[與通知相關的類名] + [Did | Will] + [UniquePartOfName] + Notification,例:

1
2
3
4
5
Objective-C
NSApplicationDidBecomeActiveNotification
NSWindowDidMiniaturizeNotification
NSTextViewDidChangeSelectionNotification
NSColorPanelColorDidChangeNotification

臨時變量命名

  • 臨時變量能夠寫得很短,如 i、k、vc 這樣;
  • 臨時變量可使用匈牙利前綴,但數據類型不能夠做爲前綴:
1
2
3
4
5
// OK
wCell, vcMaster, vToolbar

// 糟糕,數據類型做爲前綴
bool_switchState, floatBoxHeight

推薦的前綴:

前綴 含義
ix 序號,起始爲0
in 序號(天然數範圍),起始爲1
if 類型爲浮點的「序號」
x 座標
y 座標
w 寬度
h 高度
vc 視圖控制器
v 視圖

常量命名

  • 儘可能避免使用預處理命令#define來定義常量,由於#define定義的 常量是全局的,會將工程中全部定義使用的標示符替換爲定義的常量,可能由於定義的標示符重複而引發問題,且不易排查。 
  • 儘可能使用static const來進行常量的定義,好處是能夠明顯地知道常量的類型,且避免用預處理命令#define引發的問題 
  • 用static const進行常量的定義的時候,首寫字母要大寫,並加小寫字母前綴k
  • 對於枚舉常量,使用typedef NS_ENUM,對於位掩碼常量,使用typedef NS_OPTION,這兩個宏已經對C和C++進行了不一樣的枚舉處理 

除以上規則約定外,其餘常量約定了如下前綴:

前綴 含義
k 宏常量
CDEN Core Data entity name
UDk User Default key
KCk Key Chain key
APIURL 接口地址

另見:常量管理

資源命名

圖片資源在放入xcassets中相應視圖控制器文件夾的基礎上,根據[相應的功能] + [Btn | BtnClick | Icon],例:

1
2
3
UserAvatarDefaultIcon
PostCommentBtn
ClickShowWebsiteBtn

大小寫

  • 類名採用大駝峯(UpperCamelCase
  • 類成員、方法小駝峯(lowerCamelCase
  • 局部變量大小寫首選小駝峯,也可以使用小寫下劃線的形式(snake_case
  • C函數的命名用大駝峯

縮寫

可使用普遍使用的縮寫,如 URLJSON,而且縮寫要大寫。但像將download簡寫爲dl這種是不能夠的。

1
2
3
4
5
6
7
8
9
10
11
Objective-C
// OK
ID, URL, JSON, WWW

// 糟糕
id, Url, json, www

destinationSelection // OK
destSel // 糟糕
setBackgroundColor: // OK
setBkgdColor: // 糟糕

其餘

i,j專用於循環標號

爲私有方法命名不要直接以「」開頭,而應以「命名空間」開頭。

代碼格式化

空格

類方法聲明在方法類型與返回類型之間要有空格。

1
2
3
4
5
6
Objective-C
// 糟糕
-(void)methodName:(NSString *)string;

// OK
- (void)methodName:(NSString *)string;

條件判斷的括號內側不該有空格。

1
2
3
4
5
6
7
8
9
// 糟糕
if ( a < b ) {
// something
}

// OK
if (a < b) {
// something
}

關係運算符(如 >=!=)和邏輯運算符(如 &&||)兩邊要有空格。

1
2
3
4
5
// OK
(someValue > 100)? YES : NO

// OK
(items)?: @[]

二元算數運算符兩側是否加空格不肯定,根據狀況本身定。一元運算符與操做數以前沒有空格。

多個參數逗號後留一個空格(這也符合正常的西文語法)。

花括號

方法的花括號推薦另起一行。方法內部須要寫在一行。

1
2
3
4
5
6
7
8
9
- (void)methodName:(NSString *)string {
↑空格 ↑空格,推薦花括號在一行
if () {
空格↑ ↑空格,花括號不要另起一行
}
else {
要換行↑ ↑空格,花括號不要另起一行
}
}

動機

Xcode 默認的花括號位置是這樣的:方法內部的各類補全都是在同一行的;方法定義的比較混亂,默認模版另起一行,但從 Interface Builder 中連線生成的方法在同一行的。

考慮到 Xcode 的默認行爲,方法內部要另起一行,方法所在行不強制定死。另外,模版能夠定製,而 IB 生成的代碼不可定製,因此不另起一行的寫法優先。

另起一行的寫法在代碼摺疊後很是難看。

空行

相對獨立的程序塊之間、變量說明以後必須加空行。

折行

與多數其餘規範不一樣,不建議手動折行。

動機

手動折行的效果嚴重寬度依賴於窗口寬度——窗口過寬浪費寶貴的屏幕空間,較窄時可能沒法閱讀。並且 Xcode 自動折行的效果仍是不錯的。

代碼組織

  • 函數長度(行數)不該超過100行。例外:對於順序執行的初始化函數,若是其中的過程沒有提取爲獨立方法的必要,則沒必要限制長度。
  • 單個文件方法數不該超過30個
  • 不要按類別排序(如把IBAction放在一塊),應按任務把相關的組合在一塊兒
  • 禁止出現超過兩層循環的代碼,用函數或block替代。

儘早返回錯誤:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
// 爲了簡化示例,沒有錯誤處理,並使用了僞代碼

// 糟糕的例子
- (Task *)creatTaskWithPath:(NSString *)path {
Task *aTask;
if ([path isURL]) {
if ([fileManager isWritableFileAtPath:path]) {
if (![taskManager hasTaskWithPath:path]) {
aTask = [[Task alloc] initWithPath:path];
}
else {
return nil;
}
}
else {
return nil;
}
}
else {
return nil;
}
return aTask;
}

// 改寫的例子
- (Task *)creatTaskWithPath:(NSString *)path {
if (![path isURL]) {
return nil;
}

if (![fileManager isWritableFileAtPath:path]) {
return nil;
}

if ([taskManager hasTaskWithPath:path]) {
return nil;
}

Task *aTask = [[Task alloc] initWithPath:path];
return aTask;
}

禁止在類的 interface 中定義任何 iVar 成員,只容許使用屬性,但能夠在特定情形中使用屬性生成的 iVar。

儘可能老是使用點操做符訪問屬性,而不是屬性生成的 iVar 變量。如下情形除外:

  • 明確要避免修改產生 KVO 通知的;
  • 需重寫屬性 getter 或 setter 的;
  • 性能分析肯定使用屬性會致使性能不可接受的;
  • 多線程環境中,爲防止互斥一次進行多個修改的;
  • init、dealloc 方法中。

動機

若是使用 iVar,不少狀況要特殊處理,容易出錯。老是使用成員,規則簡單,不易出問題。

直接訪問 iVar 的 block 會 retain iVar 所屬的對象,這點很容易被忽略

定義和使用 iVar 都會產生編譯警告,只不過默認設置沒啓用這兩個警告

Property attributes

何時使用 copy?

  • block 屬性要定義成 copy。
  • 當一個屬性賦值後不指望改變時應當用 copy,最多見的類型如 NSString、NSURL。可變類型的成員,如 NSMutableArray、NSMutableDictionary 不能定成 copy 的。

相關 Demo 可在 https://github.com/BB9z/PropertyTest 得到。

常量

除非調試用的、控制不一樣編譯模式行爲的常量可用宏外,其餘常量不得用宏定義。

常量定義示例:

1
2
3
4
5
// 頭文件
extern ushort APIFetchPageSizeDefault; // 無const,可在外部修改

// 實現文件
ushort APIFetchPageSizeDefault = 10;

註釋

使用Xcode插件VVDocumenter-Xcode能夠有效的進行編寫註釋的需求
全部的.h文件中對外的接口方法定義中必須進行註釋,而.m文件中除非已經在.h中已經註釋的方法或者是get/set方法能夠不註釋外,其他函數必須進行註釋。
修改代碼同時修改相應的註釋,以保證註釋與代碼的一致性。再也不有用的註釋要刪除。
最後一點,儘可能讓代碼能夠自表述,而不是依賴註釋。

註釋應該表達那些代碼沒有表達以及沒法表達的東西。若是一段註釋被用於解釋一些本應該由這段代碼本身表達的東西,咱們就應該將這段註釋當作一個改變代碼結構或編碼慣例直至代碼能夠自我表達的信號。咱們重命名那些糟糕的方法和類名,而不是去修補。咱們選擇將長函數中的一些代碼段抽取出來造成一些小函數,這些小函數的名字能夠表述原代碼段的意圖,而不是對這些代碼段進行註釋。儘量的經過代碼進行表達。你經過代碼所能表達的和你想要表達的全部事情之間的差額將爲註釋提供了一個合理的候選使用場合。對那些代碼沒法表達的東西進行註釋,而不要僅簡單地註釋那些代碼沒有表達的東西。」[^2]

塊註釋

方法內部禁止使用塊註釋。除非要臨時註釋大段代碼,通常狀況總應使用行註釋。

動機

由於塊註釋不能正確嵌套。

其餘

異常

  • 做爲被調用模塊的維護者,當被調用不當時(參數有問題、不和時宜),如何處理須要考慮(拋出異常仍是返回錯誤狀態);
  • 不要依賴 try catch,它不是代替你作檢查、填補遺漏的工具。

參考

相關文章
相關標籤/搜索