作为伴随Swift 3发布的API设计指南中的要求,使用Swift Markdown为代码编写注释已经进一步从一个道义提升为了一种行为准则。一方面,Markdown对开发者来说,足够熟悉,学习难度并不高;另一方面,Xcode可以在Playground和代码提示中,对Markdown注释进行漂亮的渲染,让代码在开发者之间交流起来更加容易。
从一个包含playground文件的Single View Application开始
为了演示Markdown注释在Xcode中的各种效果,我们创建一个Single View Application,并向其中添加了一个MyPlayground文件以及一个包含struct IntArray
类型的源代码文件。因为,在Xcode在Playground和项目源代码中,使用了两种不同的Markdown注释格式,我们要分别了解它们。
为了能快速切换Markdown注释在Playground中的渲染效果,我们可以按Command + ,
打开属性对话框,选择“Key Bindings”
在“Filter”中,输入Show rendered
,Xcode会为我们自动过滤出筛选的菜单项目,“Show rendered Markup”用于在Playground里,切换Markdown注释的渲染。双击右侧的空白,为它设定一个快捷键,例如:option + M
:
这样,我们就能通过设置的快捷键在Playground中快速切换Markdown的渲染效果了。接下来,我们就来看各种常用的Markdown用法。
Playground
- 包含Markdown的单行注释用
//:
表示:
//: # Heading 1
- 包含Markdown的多行注释用下面的代码表示:
/*:
* item1
* item2
* item3
*/
按Option + M
,Playground就会自动为我们渲染注释了:
Symbol documentation
如果是在Xcode项目的源代码中:
- 包含Markdown的单行注释用
///
表示:
/// A **demo** function
func demo() {}
- 包含Markdown的多行注释用下面的代码表示:
/**
* item1
* item2
* item3
*/
func demo1() {
}
我们这样添加的注释会显示在哪呢?主要有两个地方,Apple管它们叫做symbol documentation,我们可以用两种方式来查阅它:
- 把光标放到
demo1
所在的行上,按住option
点一下,就会弹出这个函数的说明,可以看到Xcode已经把markdown注释渲染了;
- 按
Option + Command + 2
打开Quick Help Inspector,保持光标在demo1()所在行,同样,我们可以看到被渲染过的Markdown注释;
简单来说,在Playground里编写markdown注释时,注释起始的第三个字符用
:
,在项目源代码中编写markdown注释时,注释起始的第三个字母分别用/
和*
。
常用的注释范式
接下来,我们看来一些在Playground和项目代码中经常会用到的注释范式。至于其中用到的Markdown语法,大家应该都比较熟悉,我们就不一一去解释了。大家也可以在这里查看Apple官方的Swift markdown语法说明。
标记重要事项
当我们用Playground告知开发者代码中的重要事项时,可以采用下面这种类似的方式进行注释:
/*:
> # 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)
*/
- 一行简短的重要提示标题;
- 一段内容摘要;
- 一个注意事项列表;
- 一个提供更多内容的链接;
在Playground里,上面的注释可以被渲染成这样:
如果我们要把上面的注释放在项目代码里,出了要使用/**
开始外,我们还要去掉第一行的>
,因为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)
*/
这样,它看起来,就是这样的:
有关函数自身的注释范式,稍后我们会看到。接下来,我们先看一个简化注释输入的方法。
在Playground之间跳转
有时,为了演示一个项目的不同用法和功能,我们可能会在项目中使用多个Playground文件。为了方便在注释中浏览,我们可以在Playground markdown注释中,添加文件跳转链接。
选中项目中的MyPlayground,点击右键,选择“New Playground Page”,添加2个新的page进来,我们把这些页面分别命名成Page1 / Page2 / Page3。
在新添加进来的page2和page3里,先分别添加一个标题注释以方便区分它们。然后我们可以看到,在Playground页面的头部和尾部,Xcode已经为我们自动添加了两个链接:
//: [Previous](@previous)
//: [Next](@next)
按Option + M
,切换到渲染模式,分别点击页面上的Previous和Next链接,就会发现可以在页面间前后跳转了。实际上,这里用了两个关键字,@previous
和@next
,Xcode会自动把它们渲染成跳转到项目文件列表中前、后两个文件的链接。
当然,我们也可以实现跨文件跳转,打开Page1,这次在括号里写上要跳转到的目标Playground页面的名字:
//: [To Page3](Page3)
再切换到渲染模式,点击“To Page3”,就可以跳转到相应的Playground页面了。接下来,我们来看如何通过Xcode Code Snippet Library简化复杂注释的输入。
Code Snippet Library
首先,把之前我们用过的注释块,抽象成一个内容模板:
/*:
> # IMPORTANT: <#something important#>
<#General description#>
1\. <#item1#>
1\. <#item2#>
1\. <#item3#>
---
[More info - <#ref#>](<#Link#>)
*/
基本内容和之前还是一样的,只不过,我们把其中需要输入内容的地方用一对<# ... #>
包围了起来,这用于告诉Xcode,这些内容是需要每次用户单独输入的。
其次,我们选中要添加的代码块,先不要着急拖动,按住等待一会儿,直到鼠标从输入状态变回指针状态;
第三、把代码块拖动到Code Snippet Library:
这样,在Code Snippet Library里,就会多出来一项,表示我们新添加的代码片段,在图标的左下角,左右一个“User”字样,表示这是我们自定义的代码片段:
第四、点击“Edit”按钮,编辑一下这个代码片段:
其中:
-
Title
表示代码片段的名称; -
Summary
表示代码片段的简单说明; -
Platform
表示代码片段在 iOS / macOS / watchOS / tvOS / All 中使用; -
Language
表示代码片段生效的语言; -
Completion Shortcut
表示如何调出这个代码片段,在这里我们选择输入importantnote
,当然你可以设置成任何方便使用的内容; -
Completion Scopes
表示上面设置的Completion Shortcut
生效范围,All表示在任意位置都生效;
设置完成后,点击"Done"。这样当我们在Swift代码里输入importantnote
的时候,就能调出需要的注释片段,我们只要直接设置其中的内容就好了。
接下来,我们将看到一些常用的注释范式,它不仅可以让代码易于维护和交流,就像API设计指南中的描述的那样,可以有效的激发我们的设计灵感。
标记自定义类型的常用范式
对于一个自定义类型来说,我们要在注释中说明以下问题:
- 一句话描述;
- 类型主要功能;
- 常用的初始化方法以及拷贝语义;
- 补充说明;
而对这些问题的阐述,正是我们设计这个类型的过程。因此,在编码前设计注释文档,可以很好的帮我们整理设计思路。
例如,我们创建一个包含整数的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渲染出来是这样的:
而把这段注释移植到Xcode项目代码中,在Quick Help中看到的结果是这样的:
当然,你可以任意在注释里添加希望其它开发者了解的内容,例如访问、存取、修改数组的方法等。也可以通过Snippet library,把它保存起来。
在上面的注释里,有几个用法是要特别说明下的:
- 我们可以在注释中使用
single line of code
来插入单行代码; - 在注释中插入代码块时,代码块的缩进要和当前最近的一个内容缩进有4个以上的空格,否则Xcode不会识别;
- 我们在注释中使用了两个用
-
开始的标记,它们叫做callout,实际上你可以选择使用加号、减号或乘号来表示一个callout。Xcode可以识别它们,并突出显示其中的内容。大家可以在这里找到所有的callout元素列表。要说明的是,并不是所有callout都可以同时在Playground和Quick Help中使用,选择的时候,要注意这点; - 最后,我们可以使用三个及以上的
-
,表示一条分割线,用来区分正文和内容引用的部分;
标记函数或方法的常用范式
通常,对一个方法的描述,更多是用在Quick Help里。对于一个函数来说,最重要的内容无非有以下:
- 一句话功能描述;
- 常见应用场景;
- 参数;
- 返回值;
- 时间复杂度;
如果我们要在上面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.
通常,多行的代码注释也可以使用这种多个单行注释拼接的形式来编写,因为有时多行缩进的不同,会让整个注释从头部看起来比较混乱(大家可以对比下前面我们对IntArray
的注释),而使用多个///
则看起来会整齐一些。
在这个例子里,我们使用了三个callout,分别表示了方法的参数、返回值和时间复杂度,如果算法复杂度是O(1),那默认是可以省略的。
在Quick Help里,它看上去是这样的:
标记属性的常用范式
关于属性的注释,我们只强调一点,就是对于computed property来说,如果它的算法复杂度不是O(1),必须在注释中予以说明。因为对于绝大多数人来说,不会预期访问一个属性会带来严重的性能开销。
以上,就是在Swift 3 API设计指南中,关于注释的内容。在编码前,用写文档的方式来编写注释,可能在初期会让我们觉得不适应,或者觉得没必要,但至少在设计一个新的类型或方法时,尝试着去做它们,它会让你明确类型表达的语意,理清方法功能的边界,进而让你的代码,更加易于理解和交流。