Cocoa 代碼註釋與文檔生成

Cocoa Documentation.png
Cocoa Documentation.png

Cocoa 代碼註釋與文檔生成

本文的文檔規範部分的內容參考自:NSHipster 的 Swift Documentation 做者 & Nate Cookhtml

本文知識目錄

Cocoa 代碼註釋與文檔生成目錄.png
Cocoa 代碼註釋與文檔生成目錄.png

背景

曾經覺得好的代碼是能夠自我解釋不須要註釋的,後來發現不是這樣的。就算是我的項目,代碼註釋的重要性也是毋庸置疑。畢竟人的記憶只有七秒!git

一個開發者是從其餘語言轉到 Cocoa 開發,大都會被它冗長的代碼所 😺 到,Objective-C 的冗長使得其代碼就是有效的自我說明。有如 stringByAppendingPathExtension: 以及參數的顯式類型,這樣的方法通常不會給人留下太多的想象空間。不過 Swift 就有所不一樣了。github

但即便是自我說明的代碼也能夠經過說明文檔加以改進,並且只需少許的努力就能夠給別人產生顯著的好處。objective-c

文檔生成工具

每個現代編程語言都有註釋:由一個特殊的字符序列標記的非可執行的天然語言,如 ///**/--。使用特殊格式註釋的文檔,提供了代碼的輔助解釋和上下文,能夠用編譯工具提取和解析。編程

從 00 年代早期,Headerdoc 就一直做爲蘋果首選的文檔標準。從 Perl 腳本解析勉強的 Javadoc 註釋做爲出發點,Headerdoc 最終成爲了蘋果在線文檔及 Xcode 中的開發者文檔的後臺引擎。json

HeadDoc風格的文檔
HeadDoc風格的文檔

隨着 WWDC 2014 的發佈,開發者文檔被翻修並從新設計,包含了 Swift 和 Objective-C 的支持。swift

Apple 新風格的文檔
Apple 新風格的文檔

appledoc

相似 Javadoc, appledoc 可以從 .h 文件生成 HTML 和 Xcode 兼容的 .docset 文檔。xcode

Doxygen

主要用於 C++,但通常在 iOS / OS X 的開發者社區不很受待見。ruby

Jazzy

支持 Objective-C 和 Swift,經過對 Clang 和 SourceKit 的 hook 方式獲取代碼 AST 樹來精準獲取註釋。markdown

Cocoa 文檔規範

就像許多蘋果開發者生態系統同樣,Swift 改變了一切。 本着 「與時俱進,與新共存」 的精神,Xcode 7 將Headerdoc 換成了粉絲最喜歡的 Markdown,尤爲是 Swift 風格的 Markdown。

Xcode 中生成 document 模版的快捷鍵爲 option(alt) + cmd + /

Xcode 添加註釋的快捷方式
Xcode 添加註釋的快捷方式

下面爲最基本的 Cocoa 註釋。

Objective-C 風格的註釋:

/// Let's say something
/// /// @param bar I'm paramter /// @return I'm return value - (id)fooBar:(NSString *)bar; 複製代碼

Swift 風格的代碼註釋:

///
/// Let's say something /// /// - Parameter bar: I'm paramter /// - Returns: I'm return value func foo(bar: String) -> AnyObject { ... } 複製代碼
Swift 註釋的提示效果
Swift 註釋的提示效果

基本標記規則

文檔註釋經過使用 /** ... */ 的多行註釋或 ///... 的單行註釋來進行區分。在註釋塊裏面的內容支持標準的 Markdown 語法,規則以下:

  • 段落由空行分隔
  • 無序列表可由多個項目符號字符組成:-+*
  • 有序列表使用阿拉伯數字 1,2,3,… 後跟一個點符 1. 或右括號 1) 或兩側括號括起來 (1)
  • 標題使用 # 做爲前綴,內容的分割線使用 ---
  • 支持 連接圖片,會將基於Web的圖像下載並直接在 Xcode 中顯示
/**
 Hello, documentation   [土土Edmond木](https://looseyi.github.io/)   # Lists   You can apply *italic*, **bold**, or `code` inline styles.   ---   # Unordered Lists  - Lists are great,  - but perhaps don't nest;  - Sub-list formatting...  - ...isn't the best.  ---   ## Ordered Lists  1. Ordered lists, too,  2. for things that are sorted;  3. Arabic numerals  4. are the only kind supported.  */ 複製代碼

預覽效果

Markdown 註釋的樣式效果.png
Markdown 註釋的樣式效果.png

摘要和說明

文檔註釋的開頭部分將成爲 「摘要「 Summary,餘下的其餘內容將一塊兒納入 「討論」 Discussion

若是文檔註釋以段落之外的任何內容開頭,則其全部內容都將放入討論中。

參數和返回值

Xcode能夠識別一些特殊字段,並使它們與符號描述分開。 當將 ParameterReturnThrows 置爲項目符號項後帶上冒號 : 時,它們會在快捷提示和幫助文檔中與 討論 分段。

  • Parameters: 行首以 - Parameter <param name>: 開始並帶上參數的描述
  • Return values: 行首以 Returns: 開始並帶上返回值的描述
  • Thrown errors: 行首以 Throws: 並配上可能拋出的異常描述,因爲 Swift 不會對超出錯誤一致性的拋出錯誤進行類型檢查,所以正確記錄錯誤尤其重要。

本文咱們基本以 Swift 代碼做爲示例。若是代碼是 Objective-C,格式稍微有一些區別。

以返回值的關鍵字爲例。在 Swift 中,咱們編寫 -return :,但在 Objective-C 中,您將編寫 @return。 有關更多詳細信息,請參見 Apple 的 HeaderDoc 用戶指南

/**
 Creates a personalized greeting for a recipient.   - Parameter recipient: The person being greeted.   - Throws: `MyError.invalidRecipient`  if `recipient` is "Derek"  (he knows what he did).   - Returns: A new string saying hello to `recipient`.  */ func greeting(to recipient: String) throws -> String {  guard recipient != "Derek" else {  throw MyError.invalidRecipient  }   return "Greetings, \(recipient)!" } 複製代碼

若是你的方法有多個參數的話,能夠經過 Parameters: 字段加上無序列表符來註釋:

/// Returns the magnitude of a vector in three dimensions
/// from the given components. /// /// - Parameters: /// - x: The *x* component of the vector. /// - y: The *y* component of the vector. /// - z: The *z* component of the vector. func magnitude3D(x: Double, y: Double, z: Double) -> Double {  return sqrt(pow(x, 2) + pow(y, 2) + pow(z, 2)) } 複製代碼

其餘字段

除了上面的經常使用字段,Swift 風格的 Markdown 還定義了其餘幾個字段:

類型
Algorithm/Safety Information Precondition Postcondition Requires Invariant Complexity Important Warning
Metadata Author Authors Copyright Date SeeAlso Since Version
General Notes & Exhortations Attention Bug Experiment Note Remark ToDo

能夠按如下方式鬆散地組織它們,每一個字段在快速幫助中均顯示爲粗體標題,後跟一段文本:

- 字段名: 字段描述的內容從下一行開始

代碼塊

文檔註釋也支持嵌入 Code 來演示功能的正確用法或實現細節。插入代碼能夠經過添加三個反引號 ``` :

/**
 The area of the `Shape` instance.   Computation depends on the shape of the instance.  For a triangle, `area` is equivalent to:  ```  let height = triangle.calculateHeight()  let area = triangle.base * height / 2  ``` */ var area: CGFloat { get } 複製代碼

或者三個反波浪號 ~~~:

/**
 The perimeter of the `Shape` instance.   Computation depends on the shape of the instance, and is  equivalent to:   ~~~  // Circles:  let perimeter = circle.radius * 2 * Float.pi  // Other shapes:  let perimeter = shape.sides.map { $0.length }  .reduce(0, +)  ~~~  */ var perimeter: CGFloat { get } 複製代碼

MARK / TODO / FIXME

在 Objective-C 中,咱們經過預處理器指令 #pragma mark 將功能劃分爲有意義的,易於導航的部分。 在 Swift中,能夠經過註釋 // MARK: 實現相同的功能。以及咱們經常使用的其餘 tag:

  • // MARK:
  • // TODO:
  • // FIXME:
  • // NOTE:

爲了展現這些新標籤的實際做用,下面介紹瞭如何擴展 Bicycle 類以採用 CustomStringConvertible 協議並實現 description屬性。

Mark 分割效果
Mark 分割效果
// MARK: - CustomStringConvertible
 extension Bicycle: CustomStringConvertible {  public var description: String {  var descriptors: [String] = []  ...   // FIXME: Use a distance formatter  descriptors.append("...")   // TODO: Allow bikes to be named?  return descriptors.joined(separator: ", ")  } } 複製代碼

給一個類添加完整的註釋

/// 🚲 A two-wheeled, human-powered mode of transportation.
class Bicycle {  /// Frame and construction style.  enum Style {  /// A style for streets or trails.  case road   /// A style for long journeys.  case touring   /// A style for casual trips around town.  case cruiser   /// A style for general-purpose transportation.  case hybrid  }   /// The style of the bicycle.  let style: Style   /// The size of the frame, in centimeters.  let frameSize: Int   /// The number of trips traveled by the bicycle.  private(set) var numberOfTrips: Int   /// The total distance traveled by the bicycle, in meters.  private(set) var distanceTraveled: Double   /**  Take a bike out for a spin.   Calling this method increments the `numberOfTrips`  and increases `distanceTraveled` by the value of `meters`.   - Parameter meters: The distance to travel in meters.  - Precondition: `meters` must be greater than 0.  */  func travel(distance meters: Double) {  precondition(meters > 0)  distanceTraveled += meters  numberOfTrips += 1  } } 複製代碼

完整的 🚴‍♀️ 文檔,戳這裏

Library 項目文檔的生成

接下來咱們一塊兒來實踐一下,如何使用 Jazzy 來生成項目的 API 文檔。

首先,Jazzy 支持對 Swift 和 Objective-C 的 Xcode project 項目生成文檔,對於 SwiftPM 也是近期剛支持的。咱們會逐步進階,從一個獨立的 Swift 和 ObjC 項目,再到混編項目,最後是基於 CocoaPods 來生成私有庫文檔。

爲 Swift 項目生成文檔

Jazzy 默認是解析 Swift 代碼註釋來生成文檔。咱們先嚐試爲 Alamofire 生成 HTML 文檔。

首先須要安裝 Jazzy,Jazzy 是一個 Gem 依賴庫,更多瞭解 Gem 請看 《版本管理工具及 Ruby 工具鏈環境》

[sudo] gem install jazzy
複製代碼

不過因爲 Alamofire 在項目的 Gemfile 中已經添加了 Jazzy 的依賴,因此咱們這裏使用 bundle install 來安裝。最後經過 -o Docs 將文檔輸出到項目的根目錄,完整命令以下:

$ git clone https://github.com/Alamofire/Alamofire
$ cd Alamofire $ bundle install $ bundle exec jazzy -o Docs 複製代碼

生成文檔的效果:

Jazzy 生成文檔效果.png
Jazzy 生成文檔效果.png

Jazzy 默認會讀取項目中的 README.mdREADME.markdownREADME.mdownREADME 文檔內容做爲 index page 的內容。

對於 Swift module 項目,Jazzy 在生成文檔時會自動尋找項目根目錄下的 project,而後執行 xcodebuildswift build 來生成 Clang AST,再經過 SourceKit 來生成註釋文檔。

對於支持 SwiftPM 的項目,咱們也能夠經過指定 --wift-build-tool 來生成文檔:

$ bundle exec jazzy \
 -o Docs \  --module Alamofire \  --swift-build-tool spm \  --build-tool-arguments -Xswiftc,-swift-version,-Xswiftc,5 複製代碼

爲 Objective-C 項目生成文檔

Objective-C 項目文檔的生成須要多幾個參數,這裏咱們以 AFNetworking 爲例。

clone 代碼到本地後須要修改 AFNetworking 目錄下的 Gemfile 添加 Jazzy 依賴:

source "https://rubygems.org"
 gem "fastlane" gem "cocoapods" gem "jazzy" gem "xcode-install" 複製代碼

接着安裝 jazzy 生成 docs 文檔:

$ bundle install
$ bundle exec jazzy \  -o Docs \  --objc \  --umbrella-header AFNetworking/AFNetworking.h \  --framework-root AFNetworking 複製代碼
  • --objc:指定爲 Objective-C 生成文檔
  • --umbrella-header:指定 Objective-C framework 的 Umbrella header 路徑
  • --framework-root: 指定 Objective-C framework 路徑

爲 Swift & Objective-C 混編項目生成文檔

Jazzy 對於混編項目的支持目前還不是很友好。想要生成混編代碼註釋的文檔,咱們須要事先生成 json 格式的中間產物,再傳遞給 Jazzy 來生成完整的文檔。這裏須要用到工具 sourcekitten

先看基於混編項目生成文檔的方式:

# 輸出 SourceKitter 的 Swift 文檔數據到 json
sourcekitten doc -- -workspace MyProject.xcworkspace -scheme MyScheme > swiftDoc.json  # 輸出 SourceKitter 的 Objective-C 文檔數據到 json sourcekitten doc --objc $(pwd)/MyProject/MyProject.h \  -- -x objective-c -isysroot $(xcrun --show-sdk-path --sdk iphonesimulator) \  -I $(pwd) -fmodules > objcDoc.json  # 合併 swift 和 Objective-C json 並輸出文檔 jazzy --sourcekitten-sourcefile swiftDoc.json,objcDoc.json 複製代碼

固然 Jazzy 還有其餘不少使用參數這裏就不一一介紹了,你們感興趣的能夠查看官方文檔或者使用 jazzy --help <command>

總結

  • 本文介紹瞭如何按照 Apple 的標準寫出 Swift 風格的代碼註釋,以及基於 Jazzy 來生成簡單的 HTML 文檔。
  • 也許你們平時不太注意文檔的規範和書寫,可是當咱們須要查看系統 framework 或者是開源的 API 時總以爲註釋裏的說明不夠詳盡,因此從如今開始寫好文檔吧。
  • 好文檔不只能夠能夠記錄代碼的實現思路和原理,同時也可以更好的將核心思想傳遞給接手的人,更好的維護。
  • 基本上優秀的開源庫都要很是詳盡的說明文檔,比例 Alamofire,QMUI 都很值得學習。

知識點問題梳理

這裏羅列了四個問題用來考察你是否已經掌握了這篇文章,若是沒有建議你加入**收藏 **再次閱讀:

  1. Objective-C 和 Swift 代碼註釋的區別?

  2. 對包含多個參數的方法註釋與單個參數對方法有什麼區別?

  3. 代碼註釋都支持哪些 Markdown 格式?

  4. Jazzy 支持幾種模式的文檔生成?

本文使用 mdnice 排版

相關文章
相關標籤/搜索