代码注释中使用 Markdown 并生成在线文档

背景:如果代码注释可以生成如苹果官方文档样式的在线文档,并能使用 Markdown 语法进行编辑,将是一件极好的事情,于是发现了 这篇专题。进行精简之后就有了本文。IDE 要求 Xcode7 及以上

代码注释的重要性

通过写注释,你可以:

  • 详细介绍工程中方法属性所表达的意思
  • 高亮显示方法中的参数返回值
  • 在几个月后再去看你项目,可以无障碍的知道里面的方法是用来做什么的,属性又代表了什么意思等
  • 当你把代码分享出去或制作成库时,开发者可以方便的使用你的代码
  • 使用工具比如:Jazzy,生成看上去很专业的使用指南,就像苹果官方文档一样

可以通过3种方式,来预览和查看代码文档:

  • 按住 option/alt 按钮,点击某个属性方法类名,就会弹出 Quick Help 快速预览
  • 光标移到某个属性方法类名,打开 Xcode 右边工具栏,在 Quick Help 中查看
  • 使用第三方工具,生成一份指南。比如工具 Jazzy ,它可以把所有的注释生成网页,然后把这些网页组成一个单独的网站,并保存到项目的一个子目录中

Markdown 基本语法

请参考 Markdown

开始使用 Markdown 语法

你可以对项目中的属性,方法结构体枚举协议扩展以及任何代码结构或实体进行注释。

注释块写在某个声明的上面,或者某个实体的头一行。每一行以3个斜杠(///)开头,或者所有的注释都写在下面这个块中:

/**

*/
  • 我们来看第一个使用 Markdown 语法的代码片段,新建一个 Playground,加入以下代码:
/// This is an **awesome** documentation line for a really *useful* variable
var someVar = "This is a variable"

预览一下这个变量,预览效果如下图:


请注意单词 awesome 是加粗的,因为它被两个星号对包围。而单词 useful 是斜体,因为它被一个星号对包围。

  • 我们再来看一个例子,给函数添加注释,添加以下代码:
/**
    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 键,点击这个函数名称,就会弹出 Quick Help,预览效果如下图:


在注释中,我们使用了两个新元素,headerordered list ,同时也使用了加粗和斜体。只要你按着 Markdown 的语法要求,使用这些特殊的字符,就可以很容易的写出富文本的文档了。下图是在 Xcode 右侧工具栏的 Quick Help 中展示文档的效果:

  • 接下来,我们尝试在注释中加入 代码块,为了展示 代码块 的区域效果,需要使用符号 (`) 。在 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
}

预览效果如下图:


  • 最后,我们给 enum 添加注释,每一个 case 都有相应的注释,然后在另外一个函数中使用它。添加如下代码:
/**
    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)
}

当我们使用枚举 AlignmentOptions 中的某一个 case 的时候,Xcode 会自动显示相对应的注释,如图所示:

文档中使用关键字

有了富文本,那么再加上些 keywords 会使得文档更加完美。Xcode 会根据 keyword 渲染出对应的格式(同样适用于第三方库生成的文档),比如高亮显示 参数、返回值、某个类的作者、函数版本等。 keyword 有很多,我们只介绍最常用的几个

  • 关键字 parameter(不区分大小写)。

在 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)
}

文档预览效果如下图:

  • 关键字 Returns(记得加上冒号)。

修改上面的代码,返回 full name,文档中添加关键字 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)"
}

文档预览效果如下图:


  • 以上两个关键字 parameterReturns 是最常用的。

  • 关键字 RemarkSeeAlso

写一个函数,把一个 full name,拆分为两部分:firstnamelastname,并返回。代码如下:

/**
    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 在文档中高亮显示(吸引开发者注意),介绍代码中重要的或特殊的地方。SeeAlso 也会高亮显示,其主要是用来延伸到其他地方,或者提供一个 URL 链接,上面的代码中,引用了函数 returnFullName(_:lastname:)。文档预览效果如下:

  • 关键字 PreconditionRequires

假设上面的函数是某个库的一部分,为了让开发者正确使用这个函数,需要让开发者知道两点要求:

  • 参数 fullname 不能为 nil
  • fullname 包含 fistnamelastname,它们之间用空格隔开

更新上面的文档:

/**
    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])
}

预览文档效果如下:


  • 关键字 Todo

    在将来的某个版本,也许会去完善这个功能,比如可支持 middle name。那么用 Todo 来介绍:

 - Todo: Suppotr middle name in the next version.

还有很多其它的关键字,比如:warnings, version, author, notes。把它们都加进去,然后看一下效果:

/**
    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 这个工具非常棒,可以生成和苹果官方文档一样的在线文档,支持 swift (默认) 和 OC。详细介绍请看 github

  • 安装 Jazzy
    打开终端,输入:
sudo gem install jazzy

根据提示输入密码,进行安装。如果安装失败,请查看这个 issue。成功之后会有下图这样类似的信息:

  • 你需要一个项目
    你需要一个项目进行文档转换,可以下载 这个项目 作为转换的对象,里面包含有简单的文档说明。

  • 开始生成文档
    cd 到刚才下载的文件目录

cd the_path_of_your_project_folder

由于项目中都是用的默认internal 属性,为了包含所有的内容,在终端输入:

jazzy --min-acl internal

回车,成功之后的样子如下图:


  • 你可以使用 jazzy -help 来查看所有可以使用的适合你的参数。

  • 最后说明
    生成的文档会保存在项目目录下 docs 文件夹中。进入这个文件夹,双击打开 index.html,就可以在网页上查看生成的文档了:

  • 总结
    文档在一个项目中的地位是和重要的,尤其是多人合作开发。如果一段文档不能清晰的表达代码意思,对于开发者的效率来说是致命的。故,通过此文章,来鼓励 IT 人员尽可能详细的书写文档。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,657评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,662评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,143评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,732评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,837评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,036评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,126评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,868评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,315评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,641评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,773评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,470评论 4 333
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,126评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,859评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,095评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,584评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,676评论 2 351

推荐阅读更多精彩内容