來自 Mattt Thompson
code organization is a matter of hygiene (代碼組織是衛(wèi)生問題)
我們十分贊成這句話。清晰地組織代碼和規(guī)范地進(jìn)行定義, 是你對(duì)自己以及其他閱讀代碼的人的尊重。
一個(gè) GCC 非常模糊的特性,以及 Clang 也有的特性是,代碼塊如果在閉合的圓括號(hào)內(nèi)的話,會(huì)返回最后語句的值
NSURL *url = ({
NSString *urlString = [NSString stringWithFormat:@"%@/%@", baseURLString, endpoint];
[NSURL URLWithString:urlString];
});
這個(gè)特性非常適合組織小塊的代碼,通常是設(shè)置一個(gè)類。他給了讀者一個(gè)重要的入口并且減少相關(guān)干擾,能讓讀者聚焦于關(guān)鍵的變量和函數(shù)中。此外,這個(gè)方法有一個(gè)優(yōu)點(diǎn),所有的變量都在代碼塊中,也就是只在代碼塊的區(qū)域中有效,這意味著可以減少對(duì)其他作用域的命名污染。
#pragma mark -
是一個(gè)在類內(nèi)部組織代碼并且?guī)椭惴纸M方法實(shí)現(xiàn)的好辦法。 我們建議使用 #pragma mark -
來分離:
- (void)dealloc { /* ... */ }
- (instancetype)init { /* ... */ }
#pragma mark - View Lifecycle (View 的生命周期)
- (void)viewDidLoad { /* ... */ }
- (void)viewWillAppear:(BOOL)animated { /* ... */ }
- (void)didReceiveMemoryWarning { /* ... */ }
#pragma mark - Custom Accessors (自定義訪問器)
- (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
// ... 重載來自 ZOCSuperclass 的方法
#pragma mark - NSObject
- (NSString *)description { /* ... */ }
上面的標(biāo)記能明顯分離和組織代碼。你還可以用 cmd+Click 來快速跳轉(zhuǎn)到符號(hào)定義地方。 但是小心,即使 paragma mark 是一門手藝,但是它不是讓你類里面方法數(shù)量增加的一個(gè)理由:類里面有太多方法說明類做了太多事情,需要考慮重構(gòu)了。
在 http://raptureinvenice.com/pragmas-arent-just-for-marks 有很好的關(guān)于 pragma 的討論了,在這邊我們?cè)僮霾糠终f明。
大多數(shù) iOS 開發(fā)者平時(shí)并沒有和很多編譯器選項(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
來動(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)存泄露,你可以通過加入這些代碼忽略這些警告
#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 檢查的。這確保我們沒有全局禁用。如果全局禁用,可能會(huì)導(dǎo)致錯(cuò)誤。
全部的選項(xiàng)可以在 The Clang User's Manual 找到并且學(xué)習(xí)。
這對(duì)表明你一個(gè)定義但是沒有使用的變量很有用。大多數(shù)情況下,你希望移除這些引用來(稍微地)提高性能,但是有時(shí)候你希望保留它們。為什么?或許它們以后有用,或者有些特性只是暫時(shí)移除。無論如何,一個(gè)消除這些警告的好方法是用相關(guān)語句進(jìn)行注解,使用 #pragma unused()
:
- (void)giveMeFive
{
NSString *foo;
#pragma unused (foo)
return 5;
}
現(xiàn)在你的代碼不用任何編譯警告了。注意你的 pragma 需要標(biāo)記到未定義的變量之下。
編譯器是一個(gè)機(jī)器人,它會(huì)標(biāo)記你代碼中被 Clang 規(guī)則定義為錯(cuò)誤的地方。但是,你總是比 Clang 更聰明。通常,你會(huì)發(fā)現(xiàn)一些討厭的代碼 會(huì)導(dǎo)致這個(gè)問題,而且不論怎么做,你都解決不了。你可以這樣明確一個(gè)錯(cuò)誤:
- (NSInteger)divide:(NSInteger)dividend by:(NSInteger)divisor
{
#error Whoa, buddy, you need to check for zero here!
return (dividend / divisor);
}
類似的,你可以這樣標(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;
}
}
所有重要的方法,接口,分類以及協(xié)議定義應(yīng)該有伴隨的注釋來解釋它們的用途以及如何使用。更多的例子可以看 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" 這樣的描述。
如果描述超出一行,你應(yīng)該用長(zhǎng)的字符串文檔: 一行斜杠和兩個(gè)星號(hào)來開始?jí)K文檔 (/**, 之后是總結(jié)的一句話,可以用句號(hào)、問號(hào)或者感嘆號(hào)結(jié)尾,然后空一行,在和第一句話對(duì)齊寫下剩下的注釋,然后用一個(gè) (*/)來結(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è)字符串文檔,除非它符合下面的所有條件:
字符串文檔應(yīng)該描述函數(shù)的調(diào)用符號(hào)和語義,而不是它如何實(shí)現(xiàn)。
當(dāng)它需要的時(shí)候,注釋應(yīng)該用來解釋特定的代碼做了什么。所有的注釋必須被持續(xù)維護(hù)或者干脆就刪除。
塊注釋應(yīng)該被避免,代碼本身應(yīng)該盡可能就像文檔一樣表示意圖,只需要很少的打斷注釋 例外: 這不能適用于用來產(chǎn)生文檔的注釋
一個(gè)類的文檔應(yīng)該只在 .h 文件里用 Doxygen/AppleDoc 的語法書寫。 方法和屬性都應(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;
更多建議: