01-禪與 Objective-C 編程藝術(shù)之條件語(yǔ)句與命名
02-禪與 Objective-C 編程藝術(shù)之類(lèi)
03-禪與 Objective-C 編程藝術(shù)之Categories/Protocols/NSNotification
美化代碼
空格
縮進(jìn)使用 4 個(gè)空格掠哥。 永遠(yuǎn)不要使用 tab, 確保你在 Xcode 的設(shè)置里面是這樣設(shè)置的。
方法的大括號(hào)和其他的大括號(hào)(if/else/switch/while 等) 總是在同一行開(kāi)始,在新起一行結(jié)束。
推薦:
if (user.isHappy) {
//Do something
}
else {
//Do something else
}
不推薦:
if (user.isHappy)
{
//Do something
} else {
//Do something else
}
- 方法之間應(yīng)該要有一個(gè)空行來(lái)幫助代碼看起來(lái)清晰且有組織抵代。 方法內(nèi)的空格應(yīng)該用來(lái)分離功能,但是通常不同的功能應(yīng)該用新的方法來(lái)定義恕酸。
- 優(yōu)先使用 auto-synthesis奉呛。但是如果必要的話(huà), @synthesize and @dynamic
- 在實(shí)現(xiàn)文件中的聲明應(yīng)該新起一行珊搀。
- 應(yīng)該總是讓冒號(hào)對(duì)齊冶忱。有一些方法簽名可能超過(guò)三個(gè)冒號(hào),用冒號(hào)對(duì)齊可以讓代碼更具有可讀性境析。即使有代碼塊存在囚枪,也應(yīng)該用冒號(hào)對(duì)齊方法。
推薦:
[UIView animateWithDuration:1.0
animations:^{
// something
}
completion:^(BOOL finished) {
// something
}];
不推薦:
[UIView animateWithDuration:1.0 animations:^{
// something
} completion:^(BOOL finished) {
// something
}];
如果自動(dòng)對(duì)齊讓可讀性變得糟糕劳淆,那么應(yīng)該在之前把 block 定義為變量链沼,或者重新考慮你的代碼簽名設(shè)計(jì)。
換行
本指南關(guān)注代碼顯示效果以及在線(xiàn)瀏覽的可讀性沛鸵,所以換行是一個(gè)重要的主題括勺。
舉個(gè)例子:
self.productsRequest = [[SKProductsRequest alloc] initWithProductIdentifiers:productIdentifiers];
一個(gè)像上面的長(zhǎng)行的代碼在第二行以一個(gè)間隔(2個(gè)空格)延續(xù)
self.productsRequest = [[SKProductsRequest alloc]
initWithProductIdentifiers:productIdentifiers];
括號(hào)
在以下的地方使用 Egyptian風(fēng)格 括號(hào) (譯者注:又稱(chēng) K&R 風(fēng)格缆八,代碼段括號(hào)的開(kāi)始位于一行的末尾,而不是另外起一行的風(fēng)格疾捍。關(guān)于為什么叫做 Egyptian Brackets奈辰,可以參考 http://blog.codinghorror.com/new-programming-jargon/ )
- 控制語(yǔ)句 (if-else, for, switch)
非 Egyptian 括號(hào)可以用在:
- 類(lèi)的實(shí)現(xiàn)(如果存在)
- 方法的實(shí)現(xiàn)
代碼組織
來(lái)自 Mattt Thompson
code organization is a matter of hygiene (代碼組織是衛(wèi)生問(wèn)題)
我們十分贊成這句話(huà)。清晰地組織代碼和規(guī)范地進(jìn)行定義, 是你對(duì)自己以及其他閱讀代碼的人的尊重乱豆。
利用代碼塊
一個(gè) GCC 非常模糊的特性奖恰,以及 Clang 也有的特性是,代碼塊如果在閉合的圓括號(hào)內(nèi)的話(huà)宛裕,會(huì)返回最后語(yǔ)句的值
NSURL *url = ({
NSString *urlString = [NSString stringWithFormat:@"%@/%@", baseURLString, endpoint];
[NSURL URLWithString:urlString];
});
Pragma
Pragma Mark
#pragma mark -
是一個(gè)在類(lèi)內(nèi)部組織代碼并且?guī)椭惴纸M方法實(shí)現(xiàn)的好辦法瑟啃。 我們建議使用 #pragma mark -
來(lái)分離:
- 不同功能組的方法
- protocols 的實(shí)現(xiàn)
- 對(duì)父類(lèi)方法的重寫(xiě)
- (void)dealloc { /* ... */ }
- (instancetype)init { /* ... */ }
#pragma mark - View Lifecycle (View 的生命周期)
- (void)viewDidLoad { /* ... */ }
- (void)viewWillAppear:(BOOL)animated { /* ... */ }
- (void)didReceiveMemoryWarning { /* ... */ }
#pragma mark - Custom Accessors (自定義訪(fǎng)問(wèn)器)
- (void)setCustomProperty:(id)value { /* ... */ }
- (id)customProperty { /* ... */ }
#pragma mark - IBActions
- (IBAction)submitData:(id)sender { /* ... */ }
#pragma mark - Public
- (void)publicMethod { /* ... */ }
#pragma mark - Private
- (void)zoc_privateMethod { /* ... */ }
#pragma mark - UITableViewDataSource
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { /* ... */ }
#pragma mark - ZOCSuperclass
// ... 重載來(lái)自 ZOCSuperclass 的方法
#pragma mark - NSObject
- (NSString *)description { /* ... */ }
上面的標(biāo)記能明顯分離和組織代碼。你還可以用 cmd+Click 來(lái)快速跳轉(zhuǎn)到符號(hào)定義地方揩尸。 但是小心蛹屿,即使 paragma mark 是一門(mén)手藝,但是它不是讓你類(lèi)里面方法數(shù)量增加的一個(gè)理由:類(lèi)里面有太多方法說(shuō)明類(lèi)做了太多事情疲酌,需要考慮重構(gòu)了蜡峰。
關(guān)于 pragma
在 http://raptureinvenice.com/pragmas-arent-just-for-marks/ 有很好的關(guān)于 pragma 的討論了,在這邊我們?cè)僮霾糠终f(shuō)明朗恳。
大多數(shù) iOS 開(kāi)發(fā)者平時(shí)并沒(méi)有和很多編譯器選項(xiàng)打交道湿颅。一些選項(xiàng)是對(duì)控制嚴(yán)格檢查(或者不檢查)你的代碼或者錯(cuò)誤的。有時(shí)候粥诫,你想要用 pragma 直接產(chǎn)生一個(gè)異常油航,臨時(shí)打斷編譯器的行為。
當(dāng)你使用ARC的時(shí)候怀浆,編譯器幫你插入了內(nèi)存管理相關(guān)的調(diào)用谊囚。但是這樣可能產(chǎn)生一些煩人的事情。比如你使用 NSSelectorFromString
來(lái)動(dòng)態(tài)地產(chǎn)生一個(gè) selector
調(diào)用的時(shí)候执赡,ARC不知道這個(gè)方法是哪個(gè)并且不知道應(yīng)該用那種內(nèi)存管理方法镰踏,你會(huì)被提示performSelector may cause a leak because its selector is unknown
(執(zhí)行 selector 可能導(dǎo)致泄漏,因?yàn)檫@個(gè) selector 是未知的).
如果你知道你的代碼不會(huì)導(dǎo)致內(nèi)存泄露沙合,你可以通過(guò)加入這些代碼忽略這些警告
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
[myObj performSelector:mySelector withObject:name];
#pragma clang diagnostic pop
注意我們是如何在相關(guān)代碼上下文中用 pragma
停用-Warc-performSelector-leaks
檢查的奠伪。這確保我們沒(méi)有全局禁用。如果全局禁用首懈,可能會(huì)導(dǎo)致錯(cuò)誤绊率。
全部的選項(xiàng)可以在 The Clang User's Manual 找到并且學(xué)習(xí)。
忽略沒(méi)用使用變量的編譯警告
告訴你申明的變量它將不會(huì)被使用究履,這種做法很有用滤否。大多數(shù)情況下,你希望移除這些引用來(lái)(稍微地)提高性能最仑,但是有時(shí)候你希望保留它們藐俺。為什么炊甲?或許它們以后有用,或者有些特性只是暫時(shí)移除欲芹。無(wú)論如何蜜葱,一個(gè)消除這些警告的好方法是用相關(guān)語(yǔ)句進(jìn)行注解,使用 #pragma unused():
- (NSInteger)giveMeFive
{
NSString *foo;
#pragma unused (foo)
return 5;
}
現(xiàn)在你的代碼不用任何編譯警告了耀石。注意你的 pragma
需要標(biāo)記到問(wèn)題代碼之下。
明確編譯器警告和錯(cuò)誤
編譯器是一個(gè)機(jī)器人爸黄,它會(huì)標(biāo)記你代碼中被 Clang 規(guī)則定義為錯(cuò)誤的地方滞伟。但是,你總是比 Clang 更聰明炕贵。通常梆奈,你會(huì)發(fā)現(xiàn)一些討厭的代碼會(huì)導(dǎo)致這個(gè)問(wèn)題,但是暫時(shí)卻解決不了称开。你可以這樣明確一個(gè)錯(cuò)誤:
- (NSInteger)divide:(NSInteger)dividend by:(NSInteger)divisor
{
#error Whoa, buddy, you need to check for zero here!
return (dividend / divisor);
}
類(lèi)似的亩钟,你可以這樣標(biāo)明一個(gè)警告
- (float)divide:(float)dividend by:(float)divisor
{
#warning Dude, don't compare floating point numbers like this!
if (divisor != 0.0) {
return (dividend / divisor);
}
else {
return NAN;
}
}
字符串文檔
所有重要的方法,接口鳖轰,分類(lèi)以及協(xié)議定義應(yīng)該有伴隨的注釋來(lái)解釋它們的用途以及如何使用清酥。更多的例子可以看 Google 代碼風(fēng)格指南中的 File and Declaration Comments。
簡(jiǎn)而言之:有長(zhǎng)的和短的兩種字符串文檔蕴侣。
短文檔適用于單行的文件焰轻,包括注釋斜杠。它適合簡(jiǎn)短的函數(shù)昆雀,特別是(但不僅僅是)非 public 的 API:
// Return a user-readable form of a Frobnozz, html-escaped.
文本應(yīng)該用一個(gè)動(dòng)詞 ("return") 而不是 "returns" 這樣的描述辱志。
如果描述超過(guò)一行,應(yīng)改用長(zhǎng)字符串文檔:
以/**
開(kāi)始
換行寫(xiě)一句總結(jié)的話(huà)狞膘,以?
或者!
或者.
結(jié)尾揩懒。
空一行
在與第一行對(duì)齊的位置開(kāi)始寫(xiě)剩下的注釋
最后用*/
結(jié)束。
/**
This comment serves to demonstrate the format of a docstring.
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.
*/
一個(gè)函數(shù)必須有一個(gè)字符串文檔挽封,除非它符合下面的所有條件:
- 非公開(kāi)
- 很短
- 顯而易見(jiàn)
字符串文檔應(yīng)該描述函數(shù)的調(diào)用符號(hào)和語(yǔ)義已球,而不是它如何實(shí)現(xiàn)。
注釋
當(dāng)它需要的時(shí)候场仲,注釋?xiě)?yīng)該用來(lái)解釋特定的代碼做了什么和悦。所有的注釋必須被持續(xù)維護(hù)或者干脆就刪掉。
塊注釋?xiě)?yīng)該被避免渠缕,代碼本身應(yīng)該盡可能就像文檔一樣表示意圖鸽素,只需要很少的打斷注釋。 例外: 這不能適用于用來(lái)產(chǎn)生文檔的注釋
頭文檔
一個(gè)類(lèi)的文檔應(yīng)該只在.h
文件里用 Doxygen/AppleDoc
的語(yǔ)法書(shū)寫(xiě)亦鳞。 方法和屬性都應(yīng)該提供文檔馍忽。
**例子: **
/**
* Designated initializer.
*
* @param store The store for CRUD operations.
* @param searchService The search service used to query the store.
*
* @return A ZOCCRUDOperationsStore object.
*/
- (instancetype)initWithOperationsStore:(id<ZOCGenericStoreProtocol>)store
searchService:(id<ZOCGenericSearchServiceProtocol>)searchService;