1.5.Swift 3使用“Markdown方言”編寫代碼注釋

作為伴隨Swift 3發(fā)布的API設(shè)計指南中的要求,使用Swift Markdown為代碼編寫注釋已經(jīng)進(jìn)一步從一個道義提升為了一種行為準(zhǔn)則。一方面,Markdown對開發(fā)者來說,足夠熟悉,學(xué)習(xí)難度并不高;另一方面,Xcode可以在Playground和代碼提示中,對Markdown注釋進(jìn)行漂亮的渲染,讓代碼在開發(fā)者之間交流起來更加容易。

從一個包含playground文件的Single View Application開始

為了演示Markdown注釋在Xcode中的各種效果,我們創(chuàng)建一個Single View Application,并向其中添加了一個MyPlayground文件以及一個包含struct IntArray類型的源代碼文件。因為,在Xcode在Playground和項目源代碼中,使用了兩種不同的Markdown注釋格式,我們要分別了解它們。

為了能快速切換Markdown注釋在Playground中的渲染效果,我們可以按Command + ,打開屬性對話框,選擇“Key Bindings”

Key bindings

在“Filter”中,輸入Show rendered,Xcode會為我們自動過濾出篩選的菜單項目,“Show rendered Markup”用于在Playground里,切換Markdown注釋的渲染。雙擊右側(cè)的空白,為它設(shè)定一個快捷鍵,例如:option + M

Show rendered markdown

這樣,我們就能通過設(shè)置的快捷鍵在Playground中快速切換Markdown的渲染效果了。接下來,我們就來看各種常用的Markdown用法。


Playground

  • 包含Markdown的單行注釋用//:表示:
//: # Heading 1

  • 包含Markdown的多行注釋用下面的代碼表示:
/*:
  * item1
  * item2
  * item3
 */

Option + M,Playground就會自動為我們渲染注釋了:

Rendered markdown

Symbol documentation

如果是在Xcode項目的源代碼中:

  • 包含Markdown的單行注釋用///表示:
/// A **demo** function
func demo() {}

  • 包含Markdown的多行注釋用下面的代碼表示:
/**
  * item1
  * item2
  * item3
*/
func demo1() {
}

我們這樣添加的注釋會顯示在哪呢?主要有兩個地方,Apple管它們叫做symbol documentation,我們可以用兩種方式來查閱它:

  • 把光標(biāo)放到demo1所在的行上,按住option點一下,就會彈出這個函數(shù)的說明,可以看到Xcode已經(jīng)把markdown注釋渲染了;
Rendered markdown
  • Option + Command + 2打開Quick Help Inspector,保持光標(biāo)在demo1()所在行,同樣,我們可以看到被渲染過的Markdown注釋;
Rendered markdown

簡單來說,在Playground里編寫markdown注釋時,注釋起始的第三個字符用:,在項目源代碼中編寫markdown注釋時,注釋起始的第三個字母分別用/*。


常用的注釋范式

接下來,我們看來一些在Playground和項目代碼中經(jīng)常會用到的注釋范式。至于其中用到的Markdown語法,大家應(yīng)該都比較熟悉,我們就不一一去解釋了。大家也可以在這里查看Apple官方的Swift markdown語法說明。


標(biāo)記重要事項

當(dāng)我們用Playground告知開發(fā)者代碼中的重要事項時,可以采用下面這種類似的方式進(jìn)行注釋:

/*:
  > # IMPORTANT: something important you want to mention:
  A general descripiton here.
  1\. item1
  1\. item2
  1\. item3
  ---
  [More info - Access boxueio.com](https://boxueio.com)
 */

  • 一行簡短的重要提示標(biāo)題;
  • 一段內(nèi)容摘要;
  • 一個注意事項列表;
  • 一個提供更多內(nèi)容的鏈接;

在Playground里,上面的注釋可以被渲染成這樣:

Rendered markdown

如果我們要把上面的注釋放在項目代碼里,出了要使用/**開始外,我們還要去掉第一行的>,因為Quick Help不支持這樣的Markdown:

/**
  # IMPORTANT: something important you want to mention:
  A general descripiton here.
  1\. Start with
  1\. Write anything important you want to emphasize
  1\. End with  at a new line.
  ---
  [More info - Access boxueio.com](https://boxueio.com)
 */

這樣,它看起來,就是這樣的:

Rendered markdown

有關(guān)函數(shù)自身的注釋范式,稍后我們會看到。接下來,我們先看一個簡化注釋輸入的方法。


在Playground之間跳轉(zhuǎn)

有時,為了演示一個項目的不同用法和功能,我們可能會在項目中使用多個Playground文件。為了方便在注釋中瀏覽,我們可以在Playground markdown注釋中,添加文件跳轉(zhuǎn)鏈接。

選中項目中的MyPlayground,點擊右鍵,選擇“New Playground Page”,添加2個新的page進(jìn)來,我們把這些頁面分別命名成Page1 / Page2 / Page3。

在新添加進(jìn)來的page2和page3里,先分別添加一個標(biāo)題注釋以方便區(qū)分它們。然后我們可以看到,在Playground頁面的頭部和尾部,Xcode已經(jīng)為我們自動添加了兩個鏈接:

//: [Previous](@previous)

//: [Next](@next)

Option + M,切換到渲染模式,分別點擊頁面上的Previous和Next鏈接,就會發(fā)現(xiàn)可以在頁面間前后跳轉(zhuǎn)了。實際上,這里用了兩個關(guān)鍵字,@previous@next,Xcode會自動把它們渲染成跳轉(zhuǎn)到項目文件列表中前、后兩個文件的鏈接。

當(dāng)然,我們也可以實現(xiàn)跨文件跳轉(zhuǎn),打開Page1,這次在括號里寫上要跳轉(zhuǎn)到的目標(biāo)Playground頁面的名字:

//: [To Page3](Page3)

再切換到渲染模式,點擊“To Page3”,就可以跳轉(zhuǎn)到相應(yīng)的Playground頁面了。接下來,我們來看如何通過Xcode Code Snippet Library簡化復(fù)雜注釋的輸入。


Code Snippet Library

首先,把之前我們用過的注釋塊,抽象成一個內(nèi)容模板:

/*:
  > # IMPORTANT: <#something important#>
  <#General description#>
  1\. <#item1#>
  1\. <#item2#>
  1\. <#item3#>
  ---
  [More info - <#ref#>](<#Link#>)
*/

基本內(nèi)容和之前還是一樣的,只不過,我們把其中需要輸入內(nèi)容的地方用一對<# ... #>包圍了起來,這用于告訴Xcode,這些內(nèi)容是需要每次用戶單獨輸入的。

其次,我們選中要添加的代碼塊,先不要著急拖動,按住等待一會兒,直到鼠標(biāo)從輸入狀態(tài)變回指針狀態(tài);

第三、把代碼塊拖動到Code Snippet Library:

Rendered markdown

這樣,在Code Snippet Library里,就會多出來一項,表示我們新添加的代碼片段,在圖標(biāo)的左下角,左右一個“User”字樣,表示這是我們自定義的代碼片段:

Rendered markdown

第四、點擊“Edit”按鈕,編輯一下這個代碼片段:

Rendered markdown

其中:

  • Title表示代碼片段的名稱;
  • Summary表示代碼片段的簡單說明;
  • Platform表示代碼片段在 iOS / macOS / watchOS / tvOS / All 中使用;
  • Language表示代碼片段生效的語言;
  • Completion Shortcut表示如何調(diào)出這個代碼片段,在這里我們選擇輸入importantnote,當(dāng)然你可以設(shè)置成任何方便使用的內(nèi)容;
  • Completion Scopes表示上面設(shè)置的Completion Shortcut生效范圍,All表示在任意位置都生效;

設(shè)置完成后,點擊"Done"。這樣當(dāng)我們在Swift代碼里輸入importantnote的時候,就能調(diào)出需要的注釋片段,我們只要直接設(shè)置其中的內(nèi)容就好了。

接下來,我們將看到一些常用的注釋范式,它不僅可以讓代碼易于維護(hù)和交流,就像API設(shè)計指南中的描述的那樣,可以有效的激發(fā)我們的設(shè)計靈感。


標(biāo)記自定義類型的常用范式

對于一個自定義類型來說,我們要在注釋中說明以下問題:

  • 一句話描述;
  • 類型主要功能;
  • 常用的初始化方法以及拷貝語義;
  • 補充說明;

而對這些問題的闡述,正是我們設(shè)計這個類型的過程。因此,在編碼前設(shè)計注釋文檔,可以很好的幫我們整理設(shè)計思路。

例如,我們創(chuàng)建一個包含整數(shù)的struct IntArray,在Playground里添加下面的注釋:

/*:
 `IntArray` is a C-like random access collection of integers.

 ## Overview
 An `IntArray` stores values of integers in an ordered list.
 The same value can appear in an IntArray multiple times at
 different positions.

 ## Initializers
 You can create an IntArray in the following ways:

    // An empty IntArray
    var empty: IntArray = []

    // Initialzied by an array literal
    var odds: IntArray = [0, 2, 4, 6, 8]

    // Initialized by a default value
    var tenInts: IntArray = IntArray(repeating: 0, count: 10)

 ## Value semantics
 - important:
 `IntArray` object perform value type semantics. But we have the COW optimization.

 Like all value types, `IntArray` use a COW optimization.
 Multiple copies of `IntArray` share the same storage as long as
 none of the copies are modified.

 ---

 - note:
 Check [Swift Standard Library](https://developer.apple.com/reference/swift/array)
 for more informaton about arrays.
 */

上面的注釋被Xcode渲染出來是這樣的:

Rendered markdown

而把這段注釋移植到Xcode項目代碼中,在Quick Help中看到的結(jié)果是這樣的:

Rendered markdown

當(dāng)然,你可以任意在注釋里添加希望其它開發(fā)者了解的內(nèi)容,例如訪問、存取、修改數(shù)組的方法等。也可以通過Snippet library,把它保存起來。

在上面的注釋里,有幾個用法是要特別說明下的:

  • 我們可以在注釋中使用single line of code來插入單行代碼;
  • 在注釋中插入代碼塊時,代碼塊的縮進(jìn)要和當(dāng)前最近的一個內(nèi)容縮進(jìn)有4個以上的空格,否則Xcode不會識別;
  • 我們在注釋中使用了兩個用-開始的標(biāo)記,它們叫做callout,實際上你可以選擇使用加號、減號或乘號來表示一個callout。Xcode可以識別它們,并突出顯示其中的內(nèi)容。大家可以在這里找到所有的callout元素列表。要說明的是,并不是所有callout都可以同時在Playground和Quick Help中使用,選擇的時候,要注意這點;
  • 最后,我們可以使用三個及以上的-,表示一條分割線,用來區(qū)分正文和內(nèi)容引用的部分;

標(biāo)記函數(shù)或方法的常用范式

通常,對一個方法的描述,更多是用在Quick Help里。對于一個函數(shù)來說,最重要的內(nèi)容無非有以下:

  • 一句話功能描述;
  • 常見應(yīng)用場景;
  • 參數(shù);
  • 返回值;
  • 時間復(fù)雜度;

如果我們要在上面IntArray里,添加一個“返回不包括末尾N個元素的IntArray”的方法:

public func dropLast(_ n: Int) -> IntArray

它的注釋可以是這樣的:

/// Returns a subsequence containing all but the specified number of final
/// elements.
///
/// If the number of elements to drop exceeds the number of elements in the
/// collection, the result is an empty subsequence.
///
///     let numbers = [1, 2, 3, 4, 5]
///     print(numbers.dropLast(2))
///     // Prints "[1, 2, 3]"
///     print(numbers.dropLast(10))
///     // Prints "[]"
///
/// - Parameter n: The number of elements to drop off the end of the collection.
///   `n` must be greater than or equal to zero.
///
/// - Returns: A subsequence that leaves off `n` elements from the end.
///
/// - Complexity: O(*n*), where *n* is the number of elements to drop.

通常,多行的代碼注釋也可以使用這種多個單行注釋拼接的形式來編寫,因為有時多行縮進(jìn)的不同,會讓整個注釋從頭部看起來比較混亂(大家可以對比下前面我們對IntArray的注釋),而使用多個///則看起來會整齊一些。

在這個例子里,我們使用了三個callout,分別表示了方法的參數(shù)、返回值和時間復(fù)雜度,如果算法復(fù)雜度是O(1),那默認(rèn)是可以省略的

在Quick Help里,它看上去是這樣的:

Rendered markdown

標(biāo)記屬性的常用范式

關(guān)于屬性的注釋,我們只強調(diào)一點,就是對于computed property來說,如果它的算法復(fù)雜度不是O(1),必須在注釋中予以說明。因為對于絕大多數(shù)人來說,不會預(yù)期訪問一個屬性會帶來嚴(yán)重的性能開銷。

以上,就是在Swift 3 API設(shè)計指南中,關(guān)于注釋的內(nèi)容。在編碼前,用寫文檔的方式來編寫注釋,可能在初期會讓我們覺得不適應(yīng),或者覺得沒必要,但至少在設(shè)計一個新的類型或方法時,嘗試著去做它們,它會讓你明確類型表達(dá)的語意,理清方法功能的邊界,進(jìn)而讓你的代碼,更加易于理解和交流。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容