参考文档:Google Swift Style Guide

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

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

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

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

  • 参数列表可以断开。

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

  • 泛型约束可以断开。

还有一些换行的原则:

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

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

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

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

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

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

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

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

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

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

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

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

函数声明

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

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

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

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

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

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

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

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

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

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

函数调用

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

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

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

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

控制流声明

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

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

其他表达式

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

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

下面的缩进多了:

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

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