看了《禪與 Objective-C 編程藝術》,發現不少平時不注意的或注意但沒有系統總結的東西,特此記錄一下。
這次沒有整理完,后續更新會結合手里的一些其他資料整理。
新博客wossoneri.com傳送門
完整的介紹看這兩個鏈接
Google開源項目風格指南
禪與 Objective-C 編程藝術 (Zen and the Art of the Objective-C Craftsmanship 中文翻譯)
本文的意義就是找出一些我自己平時不注意的知識點進行總結,同時提出一些自己的觀點,然后自己沒事看看總結,提高編碼質量。
條件語句
條件語句體應該總是被大括號包圍。
我經常為了讓代碼看起來干凈一些,所以對于條件語句體內只有一行代碼的時候,我就會省去大括號以減少代碼行數。
像這樣:
if (!error)return success;
//or
if (!error) return success;
這樣寫很容易帶來隱患,比如增加一行代碼時,就容易對這行代碼是在語句體內還是外產生誤解導致錯誤。注釋一行代碼容易使后面一行代碼成為語句體內的內容。
所以老老實實把代碼寫規范了:
if (!error) {return success;
}
尤達表達式
星球大戰中尤達大師的講話方式,總是用倒裝的語序
文章不建議使用尤達表達式,即不要使用常量和變量比較的方式。
比如:
if (5 == count) { ...
//or
if ([@42 isEqual:myValue]) { ...
建議寫成:
if (count == 5) { ...
//or
if ([myValue isEqual:@42]) { ...
對于這一點,我有一些異議。因為對于第一種寫法,寫成5 == count
可以避免把==
寫成=
,如果按照不用尤達的寫法,當我把比較寫成賦值時count = 5
,編譯器是無法檢測出我的失誤的,而且對于這類失誤,自己debug的時候也很難發現。而對于第二種寫法使用isEqual
方法就不存在這種問題。
所以這一點見仁見智吧。面對if (5 == count)
這類條件語句,我還是偏向用尤達表達式的。
nil 和 BOOL 的檢查
因為
nil
是 解釋到NO
,所以沒必要在條件語句里面把它和其他值比較。同時,不要直接把它和YES
比較,因為YES
的定義是 1, 而BOOL
是 8 bit的,實際上是char
類型。
這一點很重要,不要在條件語句內出現YES
或者NO
,多使用非運算符。
不推薦:
if (someObject == YES) { ... // Wrong
if (myRawValue == YES) { ... // Never do this.
if ([someObject boolValue] == NO) { ...
推薦:
if (someObject) { ...
if (![someObject boolValue]) { ...
if (!someObject) { ...
黃金大道
在使用條件語句編程時,代碼的左邊距應該是一條“黃金”或者“快樂”的大道。
也就是說,不要嵌套 if
語句。使用多個 return 可以避免增加循環的復雜度,并提高代碼的可讀性。
這一點我深有感觸,在以往編碼的時候,我都很耿直的把一些方法寫到嵌套分支里。這樣寫的容易,但改代碼邏輯的時候就麻煩了。
比如:
- (void)someMethod {if ([someOther boolValue]) {//Do something important// blablabla ...}
}
這種情況下,一個是代碼看起來很復雜,另一個是改邏輯的時候,blablabla的內容都要拷貝出來,放在新修改的邏輯中去。
所以直到有一次思考優化代碼的時候,我才想起來用相反的邏輯去處理它,即使用 return。比如:
- (void)someMethod {if (![someOther boolValue]) {return;}//Do something important
}
把邏輯判斷單獨擇出來,處理代碼就直接放在函數里。不符合邏輯的 return 掉,符合邏輯的直接就會運行到處理代碼處。而且這樣寫代碼層次很清晰。
Case 語句
除非編譯器強制要求,括號在 case 語句里面是不必要的。但是當一個 case 包含了多行語句的時候,需要加上括號。
個人經驗,好像在 case 中創建實例的時候,編譯器會提醒你要為這個 case 加上括號。
switch (condition) {case 1:// ...break;case 2: {// ...// Multi-line example using bracesbreak;}case 3:// ...break;default:// ...break;
}
常量使用、枚舉和命名規范
見這篇博客:Objective-C 常量和枚舉
方法
方法名與方法類型 (-/+ 符號)之間應該以空格間隔。方法段之間也應該以空格間隔(以符合 Apple 風格)。參數前應該總是有一個描述性的關鍵詞。
這里要注意的一點是盡量少用 and 這個詞。我因為入門的時候看的書經常用 and ,感覺這樣閱讀起來比較連貫,所以也養成了寫 and 的習慣。后來感覺加 and 的確不是很好。關于看書命名這一塊推薦看一下 Swift
的函數命名,因為 Swift
設計的時候一部分考慮到了 OC 當前的設計思想,同時對舊設計思想有了一些優化。看完你就會有自己的一套理解了。
不推薦寫法:
- (void)setT:(NSString *)text i:(UIImage *)image;
- (void)sendAction:(SEL)aSelector :(id)anObject :(BOOL)flag;
- (id)taggedView:(NSInteger)tag;
- (instancetype)initWithWidth:(CGFloat)width andHeight:(CGFloat)height;
- (instancetype)initWith:(int)width and:(int)height; // Never do this.
推薦寫法:
- (void)setExampleText:(NSString *)text image:(UIImage *)image;
- (void)sendAction:(SEL)aSelector to:(id)anObject forAllCells:(BOOL)flag;
- (id)viewWithTag:(NSInteger)tag;
- (instancetype)initWithWidth:(CGFloat)width height:(CGFloat)height;
美化代碼
空格
- 縮進使用 4 個空格。 永遠不要使用 tab。可以在 Xcode 中設置按 tab 插入 4 個空格。
- 方法的大括號和其他的大括號(if/else/switch/while 等) 總是在同一行開始,在新起一行結束。
第一點就不說了,前段時間看文章說還有人用 3 個空格,8 個空格的。個人還是覺得 4 空格比較舒服。
第二點是要注意的,可能以前看的初級書都是這樣寫:
if (user.isHappy)
{//Do something
} else {//Do something else
}
但分開寫的話代碼層次關系會更明顯:
if (user.isHappy) {//Do something
}
else {//Do something else
}
- 方法之間應該要有一個空行來幫助代碼看起來清晰且有組織。 方法內的空格應該用來分離功能,但是通常不同的功能應該用新的方法來定義。
- 優先使用 auto-synthesis。但是如果必要的話, @synthesize and @dynamic
- 在實現文件中的聲明應該新起一行。
- 應該總是讓冒號對齊。有一些方法簽名可能超過三個冒號,用冒號對齊可以讓代碼更具有可讀性。即使有代碼塊存在,也應該用冒號對齊方法。
這幾點 Xcode 會幫你做一部分,比如說冒號對齊在 Xcode 里就是自動的。但還是注意一下吧。
不推薦:
[UIView animateWithDuration:1.0 animations:^{// something
} completion:^(BOOL finished) {// something
}];
推薦:
[UIView animateWithDuration:1.0animations:^{// something}completion:^(BOOL finished) {// something}];
換行
之前看過一個建議,就是一行代碼保持在 80 個字符。
后來在不同的 IDE 下編碼,發現不同的 IDE 在這一點上是一致的,就是可以設置一個字符長度的標志線,來提示代碼長度。默認長度都是在 80 個字符。這個 Xcode 好像是默認不開啟的,可以設置一下讓那條線顯示出來。
舉例:
self.productsRequest = [[SKProductsRequest alloc] initWithProductIdentifiers:productIdentifiers];
一個像上面的長行的代碼在第二行以一個間隔(2個空格)延續
self.productsRequest = [[SKProductsRequest alloc]initWithProductIdentifiers:productIdentifiers];
括號
在以下的地方使用 Egyptian風格 括號 (譯者注:又稱 K&R 風格,代碼段括號的開始位于一行的末尾,而不是另外起一行的風格。關于為什么叫做 Egyptian Brackets,可以參考 http://blog.codinghorror.com/new-programming-jargon/ )
- 控制語句 (if-else, for, switch)
非 Egyptian 括號可以用在:
- 類的實現(如果存在)
- 方法的實現
看完就不要糾結 Java 風格 or C++ 風格了...
未完...
很多內容在整理...