背景:如果代碼注釋可以生成如蘋果官方文檔樣式的在線文檔,并能使用 Markdown 語法進(jìn)行編輯,將是一件極好的事情,于是發(fā)現(xiàn)了 這篇專題。進(jìn)行精簡之后就有了本文。IDE 要求 Xcode7 及以上
代碼注釋的重要性
通過寫注釋,你可以:
- 詳細(xì)介紹工程中
類、方法、屬性所表達(dá)的意思 - 高亮顯示方法中的
參數(shù)和返回值 - 在幾個(gè)月后再去看你項(xiàng)目,可以無障礙的知道里面的
方法是用來做什么的,屬性又代表了什么意思等 - 當(dāng)你把代碼分享出去或制作成庫時(shí),開發(fā)者可以方便的使用你的代碼
- 使用工具比如:Jazzy,生成看上去很專業(yè)的使用指南,就像蘋果官方文檔一樣
可以通過3種方式,來預(yù)覽和查看代碼文檔:
- 按住 option/alt 按鈕,點(diǎn)擊某個(gè)
屬性,方法,類名,就會(huì)彈出Quick Help快速預(yù)覽 - 光標(biāo)移到某個(gè)
屬性,方法,類名,打開 Xcode 右邊工具欄,在Quick Help中查看 - 使用第三方工具,生成一份指南。比如工具 Jazzy ,它可以把所有的注釋生成網(wǎng)頁,然后把這些網(wǎng)頁組成一個(gè)單獨(dú)的網(wǎng)站,并保存到項(xiàng)目的一個(gè)子目錄中
Markdown 基本語法
請參考 Markdown
開始使用 Markdown 語法
你可以對項(xiàng)目中的
屬性,方法,類,結(jié)構(gòu)體,枚舉,協(xié)議,擴(kuò)展以及任何代碼結(jié)構(gòu)或?qū)嶓w進(jìn)行注釋。
注釋塊寫在某個(gè)聲明的上面,或者某個(gè)實(shí)體的頭一行。每一行以3個(gè)斜杠(///)開頭,或者所有的注釋都寫在下面這個(gè)塊中:
/**
*/
- 我們來看第一個(gè)使用 Markdown 語法的代碼片段,新建一個(gè) Playground,加入以下代碼:
/// This is an **awesome** documentation line for a really *useful* variable
var someVar = "This is a variable"
預(yù)覽一下這個(gè)變量,預(yù)覽效果如下圖:

請注意單詞
awesome 是加粗的,因?yàn)樗粌蓚€(gè)星號對包圍。而單詞 useful 是斜體,因?yàn)樗灰粋€(gè)星號對包圍。
- 我們再來看一個(gè)例子,給函數(shù)添加注釋,添加以下代碼:
/**
It calculates and returns the outcome of the division of the two parameters.
## Important Notes ##
1. Both parameters are **double** numbers.
2. For a proper result the second parameter *must be other than 0*
3. If the second parameter is 0 then the function will return nil.
*/
func performDivision(number1: Double, number2: Double) -> Double! {
if number2 != 0 {
return number1 / number2
}
else {
return nil
}
}
然后,按住 option 鍵,點(diǎn)擊這個(gè)函數(shù)名稱,就會(huì)彈出 Quick Help,預(yù)覽效果如下圖:

在注釋中,我們使用了兩個(gè)新元素,
header 和 ordered list ,同時(shí)也使用了加粗和斜體。只要你按著 Markdown 的語法要求,使用這些特殊的字符,就可以很容易的寫出富文本的文檔了。下圖是在 Xcode 右側(cè)工具欄的 Quick Help 中展示文檔的效果:

- 接下來,我們嘗試在注釋中加入 代碼塊,為了展示 代碼塊 的區(qū)域效果,需要使用符號 (`) 。在 Playground 中添加以下代碼:
/**
It doubles the value given as a parameter.
### Usage Example: ###
```
let single = 5
let double = doubleValue(single)
print(double)
```
* Use the `doubleValue(_:)` function to get the double value of any number.
* Only ***Int*** properties are allowed.
*/
func doubleValue(value: Int) -> Int {
return value * 2
}
預(yù)覽效果如下圖:

- 最后,我們給
enum添加注釋,每一個(gè)case都有相應(yīng)的注釋,然后在另外一個(gè)函數(shù)中使用它。添加如下代碼:
/**
My own alignment options.
```
case Left
case Center
case Right
```
*/
enum AlignmentOptions {
/// It aligns the text on the Left side.
case Left
/// It aligns the text on the Center.
case Center
/// It aligns the text on the Right Side.
case Right
}
func doSomething() {
var alignmentOption :AlignmentOptions!
alignmentOption = AlignmentOptions.Center
print(alignmentOption)
}
當(dāng)我們使用枚舉 AlignmentOptions 中的某一個(gè) case 的時(shí)候,Xcode 會(huì)自動(dòng)顯示相對應(yīng)的注釋,如圖所示:

文檔中使用關(guān)鍵字
有了富文本,那么再加上些
keywords會(huì)使得文檔更加完美。Xcode 會(huì)根據(jù)keyword渲染出對應(yīng)的格式(同樣適用于第三方庫生成的文檔),比如高亮顯示 參數(shù)、返回值、某個(gè)類的作者、函數(shù)版本等。keyword有很多,我們只介紹最常用的幾個(gè)
- 關(guān)鍵字
parameter(不區(qū)分大小寫)。
在 Playground 中添加以下代碼:
/**
This is an extremely complicated method that concatenates the first and last name and produces the full name.
- Parameter firstname: The first part of the fullname.
- parameter lastname: The last part of the fullname.
*/
func createFullName(firstname: String, lastname: String) {
let fullname = "\(firstname) \(lastname)"
print(fullname)
}
文檔預(yù)覽效果如下圖:

- 關(guān)鍵字
Returns(記得加上冒號)。
修改上面的代碼,返回 full name,文檔中添加關(guān)鍵字 Returns 如下:
/**
This is an extremely complicated method that returns the full name.
- Parameter firstname: The first part of the fullname.
- parameter lastname: The last part of the fullname.
- Returns: The full name as a string value.
*/
func returnFullName(firstname: String, lastname: String) -> String{
return "\(firstname) \(lastname)"
}
文檔預(yù)覽效果如下圖:

以上兩個(gè)關(guān)鍵字
parameter和Returns是最常用的。關(guān)鍵字
Remark和SeeAlso。
寫一個(gè)函數(shù),把一個(gè) full name,拆分為兩部分:firstname 和 lastname,并返回。代碼如下:
/**
Another complicated function.
- Parameter fullname: The fullname that will be broken into its parts.
- Returns: A *tuple* with the first and last name.
- Remark:
There's a counterpart function that contatenates the first and last name into a full name.
- SeeAlso: `returnFullName(_:lastname:)`
*/
func breakFullName(fullname: String) -> (firstname: String, lastname: String) {
let namesTuple = fullname.componentsSeparatedByString(" ")
return (namesTuple[0],namesTuple[1])
}
Remark 在文檔中高亮顯示(吸引開發(fā)者注意),介紹代碼中重要的或特殊的地方。SeeAlso 也會(huì)高亮顯示,其主要是用來延伸到其他地方,或者提供一個(gè) URL 鏈接,上面的代碼中,引用了函數(shù) returnFullName(_:lastname:)。文檔預(yù)覽效果如下:

- 關(guān)鍵字
Precondition和Requires。
假設(shè)上面的函數(shù)是某個(gè)庫的一部分,為了讓開發(fā)者正確使用這個(gè)函數(shù),需要讓開發(fā)者知道兩點(diǎn)要求:
- 參數(shù)
fullname不能為 nil -
fullname包含fistname和lastname,它們之間用空格隔開
更新上面的文檔:
/**
Another complicated function.
- Parameter fullname: The fullname that will be broken into its parts.
- Returns: A *tuple* with the first and last name.
- Remark:
There's a counterpart function that contatenates the first and last name into a full name.
- SeeAlso: `returnFullName(_:lastname:)`
- Precondition: `fullname` should not be nil.
- Requires: Both first and last name should be parts of the full name,separated with a *space character*.
*/
func breakFullName(fullname: String) -> (firstname: String, lastname: String) {
let namesTuple = fullname.componentsSeparatedByString(" ")
return (namesTuple[0],namesTuple[1])
}
預(yù)覽文檔效果如下:

-
關(guān)鍵字
Todo。在將來的某個(gè)版本,也許會(huì)去完善這個(gè)功能,比如可支持
middle name。那么用Todo來介紹:
- Todo: Suppotr middle name in the next version.
還有很多其它的關(guān)鍵字,比如:warnings, version, author, notes。把它們都加進(jìn)去,然后看一下效果:
/**
Another complicated function.
- Parameter fullname: The fullname that will be broken into its parts.
- Returns: A *tuple* with the first and last name.
- Remark:
There's a counterpart function that contatenates the first and last name into a full name.
- SeeAlso: `returnFullName(_:lastname:)`
- Precondition: `fullname` should not be nil.
- Requires: Both first and last name should be parts of the full name,separated with a *space character*.
- Todo: Support middle name in the next version.
- Warning: A wonderful **crash** will be the result of a `nil` parameter.
- Version: 1.0
- Author: Cook
- Note: Too much documentation for such a small function.
*/
func breakFullName(fullname: String) -> (firstname: String, lastname: String) {
let namesTuple = fullname.componentsSeparatedByString(" ")
return (namesTuple[0],namesTuple[1])
}

生成在線文檔
使用 Jazzy 來制作在線文檔。Jazzy 這個(gè)工具非常棒,可以生成和蘋果官方文檔一樣的在線文檔,支持 swift (默認(rèn)) 和 OC。詳細(xì)介紹請看 github
- 安裝 Jazzy
打開終端,輸入:
sudo gem install jazzy
根據(jù)提示輸入密碼,進(jìn)行安裝。如果安裝失敗,請查看這個(gè) issue。成功之后會(huì)有下圖這樣類似的信息:

你需要一個(gè)項(xiàng)目
你需要一個(gè)項(xiàng)目進(jìn)行文檔轉(zhuǎn)換,可以下載 這個(gè)項(xiàng)目 作為轉(zhuǎn)換的對象,里面包含有簡單的文檔說明。開始生成文檔
cd 到剛才下載的文件目錄
cd the_path_of_your_project_folder
由于項(xiàng)目中都是用的默認(rèn)internal 屬性,為了包含所有的內(nèi)容,在終端輸入:
jazzy --min-acl internal
回車,成功之后的樣子如下圖:

你可以使用
jazzy -help來查看所有可以使用的適合你的參數(shù)。最后說明
生成的文檔會(huì)保存在項(xiàng)目目錄下docs文件夾中。進(jìn)入這個(gè)文件夾,雙擊打開index.html,就可以在網(wǎng)頁上查看生成的文檔了:

- 總結(jié)
文檔在一個(gè)項(xiàng)目中的地位是和重要的,尤其是多人合作開發(fā)。如果一段文檔不能清晰的表達(dá)代碼意思,對于開發(fā)者的效率來說是致命的。故,通過此文章,來鼓勵(lì) IT 人員盡可能詳細(xì)的書寫文檔。