参考文档:Google Swift Style Guide

为了可读性在一些声明里可以把一行过长的代码分成几行。比如下面的情况,如果生硬的写在一行里读起来就会很困难:

  1. public func index<Elements: Collection, Element>(of element: Element, in collection: Elements) -> Elements.Index? where Elements.Element == Element, Element: Equatable {
  2. // ...
  3. }

当然断句也要遵循一定的规则。有一些单元应该连在一起,一些连接可以分开。以上面的声明为例子:

  • 最前面的函数基本名称和泛型的声明不能断开。

  • 参数列表可以断开。

  • 参数末尾的右括号和返回声明要连在一起。

  • 泛型约束可以断开。

还有一些换行的原则:

  • 参数列表如果断开,那么必须每一个参数单独一行。只有全部在一行,每个参数一行这两种形式。

  • 可以断开的语句前面要加一个缩进。

  • 如果函数声明的结尾是可以断开的单元,那么函数体的开始左花括号单独一行。

上面的例子按照规范的换行会是这样:

  1. public func index<Elements: Collection, Element>(
  2. of element: Element,
  3. in collection: Elements
  4. ) -> Elements.Index?
  5. where
  6. Elements.Element == Element,
  7. Element: Equatable
  8. { // 注意花括号换行了
  9. for current in elements {
  10. // ...
  11. }
  12. }

如果花括号换行没有换行,函数体视觉上就会容易和声明混在一起:

  1. public func index<Elements: Collection, Element>(
  2. of element: Element,
  3. in collection: Elements
  4. ) -> Elements.Index?
  5. where
  6. Elements.Element == Element,
  7. Element: Equatable { // 下面的 body 就会和声明在一个缩进里,容易看错
  8. for current in elements {
  9. // ...
  10. }
  11. }

如果带有 where 关键字的泛型约束,那么还有两条规则:

  • 如果 return 加上 where 后面的声明超过了每行长度的限制,那么从 where 开始换行,where 与原始行在同一级的缩进。

  • 如果 where 后面的约束整句还是超过了长度的限制,那么每个约束单独换行,并且结尾后带一个换行。

这样的断句样式保证了不同部分的声明可以快速、轻松的被读者通过换行和缩进识别出来,同时在整个文件中保持相同的缩进级别。这种方式防止了通过括号换行出现的锯齿效应(zig-zag effect),这种情况在其他语言很常见:

  1. public func index<Elements: Collection, Element>(of element: Element,
  2. in collection: Elements) -> Elements.Index?
  3. where Elements.Element == Element, Element: Equatable {
  4. doSomething()
  5. }

函数声明

按照上面的规则断句,我们会得到下面的代码:代码换行 - 图2

  1. public func index<Elements: Collection, Element>(
  2. of element: Element,
  3. in collection: Elements
  4. ) -> Elements.Index? where Elements.Element == Element, Element: Equatable {
  5. for current in elements {
  6. // ...
  7. }
  8. }

在协议中声明的函数的右括号可以放在与最终参数相同的行上,也可以单独列一行。

  1. public protocol ContrivedExampleDelegate {
  2. func contrivedExample(
  3. _ contrivedExample: ContrivedExample,
  4. willDoSomethingTo someValue: SomeValue)
  5. }
  6. public protocol ContrivedExampleDelegate {
  7. func contrivedExample(
  8. _ contrivedExample: ContrivedExample,
  9. willDoSomethingTo someValue: SomeValue
  10. )
  11. }

如果声明中参数、约束、返回值的某个元素非常复杂或者有多层嵌套(比如参数是一个闭包、Tuple),那么也可以把这个元素按照前面提到的规则断开。

  1. public func performanceTrackingIndex<Elements: Collection, Element>(
  2. of element: Element,
  3. in collection: Elements
  4. ) -> ( // 返回值是一个复杂的 tuple
  5. Element.Index?,
  6. PerformanceTrackingIndexStatistics.Timings,
  7. PerformanceTrackingIndexStatistics.SpaceUsed
  8. ) {
  9. // ...
  10. }

如果有一个元素类型非常复杂,建议使用 typealias 取一个别名来简化声明的复杂度。

类型和 Extension 的声明代码换行 - 图3

下面的规则适用于 class,struct,enum,extension,protocol :

  1. class MyClass:
  2. MySuperclass,
  3. MyProtocol,
  4. SomeoneElsesProtocol,
  5. SomeFrameworkProtocol
  6. {
  7. // ...
  8. }
  9. class MyContainer<Element>:
  10. MyContainerSuperclass,
  11. MyContainerProtocol,
  12. SomeoneElsesContainerProtocol,
  13. SomeFrameworkContainerProtocol
  14. {
  15. // ...
  16. }
  17. class MyContainer<BaseCollection>:
  18. MyContainerSuperclass,
  19. MyContainerProtocol,
  20. SomeoneElsesContainerProtocol,
  21. SomeFrameworkContainerProtocol
  22. where BaseCollection: Collection {
  23. // ...
  24. }
  25. class MyContainer<BaseCollection>:
  26. MyContainerSuperclass,
  27. MyContainerProtocol,
  28. SomeoneElsesContainerProtocol,
  29. SomeFrameworkContainerProtocol
  30. where
  31. BaseCollection: Collection,
  32. BaseCollection.Element: Equatable,
  33. BaseCollection.Element: SomeOtherProtocolOnlyUsedToForceLineWrapping
  34. {
  35. // ...
  36. }

函数调用

当一个函数断句换行后,每一个参数都声明在独立的一行中,跟原始行有 2 个空格缩进。和函数声明一样,函数调用结尾的右括号(没有尾闭包)既可以在参数行的末尾,也可以单独起一行。

  1. let index = index(
  2. of: veryLongElementVariableName,
  3. in: aCollectionOfElementsThatAlsoHappensToHaveALongName)
  4. let index = index(
  5. of: veryLongElementVariableName,
  6. in: aCollectionOfElementsThatAlsoHappensToHaveALongName
  7. )

如果函数调用时有尾闭包,那么尾闭包的参数必须换行后单独一行,并将参数列表用括号括起来,以便与下面的闭包区分开来。

  1. someAsynchronousAction.execute(withDelay: howManySeconds, context: actionContext) {
  2. (context, completion) in
  3. doSomething(withContext: context)
  4. completion()
  5. }

控制流声明

当一个控制流的声明需要断句换行(比如 if、guard、 while、 for),继续的下一行和前面保持一样的缩进。如果语法上是嵌套,前面加 2 个空格缩进。结尾的花括号依然可以接着末尾或单独起一行。对于 guard 要强调的是 else { 必须写在同一行。

  1. if aBooleanValueReturnedByAVeryLongOptionalThing() &&
  2. aDifferentBooleanValueReturnedByAVeryLongOptionalThing() &&
  3. yetAnotherBooleanValueThatContributesToTheWrapping() {
  4. doSomething()
  5. }
  6. if aBooleanValueReturnedByAVeryLongOptionalThing() &&
  7. aDifferentBooleanValueReturnedByAVeryLongOptionalThing() &&
  8. yetAnotherBooleanValueThatContributesToTheWrapping()
  9. {
  10. doSomething()
  11. }
  12. if let value = aValueReturnedByAVeryLongOptionalThing(),
  13. let value2 = aDifferentValueReturnedByAVeryLongOptionalThing() {
  14. doSomething()
  15. }
  16. if let value = aValueReturnedByAVeryLongOptionalThing(),
  17. let value2 = aDifferentValueReturnedByAVeryLongOptionalThingThatForcesTheBraceToBeWrapped()
  18. {
  19. doSomething()
  20. }
  21. guard let value = aValueReturnedByAVeryLongOptionalThing(),
  22. let value2 = aDifferentValueReturnedByAVeryLongOptionalThing() else {
  23. doSomething()
  24. }
  25. guard let value = aValueReturnedByAVeryLongOptionalThing(),
  26. let value2 = aDifferentValueReturnedByAVeryLongOptionalThing()
  27. else {
  28. doSomething()
  29. }
  30. for element in collection
  31. where element.happensToHaveAVeryLongPropertyNameThatYouNeedToCheck {
  32. doSomething()
  33. }

其他表达式

如果是表达式过长需要换行,下一行前面加 2 个缩进。如果换成多行,每一行都比前一行增加 2 个缩进。

  1. let result = anExpression + thatIsMadeUpOf * aLargeNumber +
  2. ofTerms / andTherefore % mustBeWrapped + (
  3. andWeWill - keepMakingItLonger * soThatWeHave / aContrivedExample)

下面的缩进多了:

  1. let result = anExpression + thatIsMadeUpOf * aLargeNumber +
  2. ofTerms / andTherefore % mustBeWrapped + (
  3. andWeWill - keepMakingItLonger * soThatWeHave / aContrivedExample)

过长的表达式还是建议重构代码,拆分为几个变量再做运算会好一点。