为了可读性在一些声明里可以把一行过长的代码分成几行。比如下面的情况,如果生硬的写在一行里读起来就会很困难:
❌public func index<Elements: Collection, Element>(of element: Element, in collection: Elements) -> Elements.Index? where Elements.Element == Element, Element: Equatable {// ...}
当然断句也要遵循一定的规则。有一些单元应该连在一起,一些连接可以分开。以上面的声明为例子:
最前面的函数基本名称和泛型的声明不能断开。
参数列表可以断开。
参数末尾的右括号和返回声明要连在一起。
泛型约束可以断开。
还有一些换行的原则:
参数列表如果断开,那么必须每一个参数单独一行。只有全部在一行,每个参数一行这两种形式。
可以断开的语句前面要加一个缩进。
如果函数声明的结尾是可以断开的单元,那么函数体的开始左花括号单独一行。
上面的例子按照规范的换行会是这样:
✅public func index<Elements: Collection, Element>(of element: Element,in collection: Elements) -> Elements.Index?whereElements.Element == Element,Element: Equatable{ // 注意花括号换行了for current in elements {// ...}}
如果花括号换行没有换行,函数体视觉上就会容易和声明混在一起:
❌public func index<Elements: Collection, Element>(of element: Element,in collection: Elements) -> Elements.Index?whereElements.Element == Element,Element: Equatable { // 下面的 body 就会和声明在一个缩进里,容易看错for current in elements {// ...}}
如果带有 where 关键字的泛型约束,那么还有两条规则:
如果 return 加上 where 后面的声明超过了每行长度的限制,那么从 where 开始换行,where 与原始行在同一级的缩进。
如果 where 后面的约束整句还是超过了长度的限制,那么每个约束单独换行,并且结尾后带一个换行。
这样的断句样式保证了不同部分的声明可以快速、轻松的被读者通过换行和缩进识别出来,同时在整个文件中保持相同的缩进级别。这种方式防止了通过括号换行出现的锯齿效应(zig-zag effect),这种情况在其他语言很常见:
❌public func index<Elements: Collection, Element>(of element: Element,in collection: Elements) -> Elements.Index?where Elements.Element == Element, Element: Equatable {doSomething()}
函数声明
按照上面的规则断句,我们会得到下面的代码:
✅public func index<Elements: Collection, Element>(of element: Element,in collection: Elements) -> Elements.Index? where Elements.Element == Element, Element: Equatable {for current in elements {// ...}}
在协议中声明的函数的右括号可以放在与最终参数相同的行上,也可以单独列一行。
✅public protocol ContrivedExampleDelegate {func contrivedExample(_ contrivedExample: ContrivedExample,willDoSomethingTo someValue: SomeValue)}public protocol ContrivedExampleDelegate {func contrivedExample(_ contrivedExample: ContrivedExample,willDoSomethingTo someValue: SomeValue)}
如果声明中参数、约束、返回值的某个元素非常复杂或者有多层嵌套(比如参数是一个闭包、Tuple),那么也可以把这个元素按照前面提到的规则断开。
✅public func performanceTrackingIndex<Elements: Collection, Element>(of element: Element,in collection: Elements) -> ( // 返回值是一个复杂的 tupleElement.Index?,PerformanceTrackingIndexStatistics.Timings,PerformanceTrackingIndexStatistics.SpaceUsed) {// ...}
如果有一个元素类型非常复杂,建议使用 typealias 取一个别名来简化声明的复杂度。
类型和 Extension 的声明
下面的规则适用于 class,struct,enum,extension,protocol :
✅class MyClass:MySuperclass,MyProtocol,SomeoneElsesProtocol,SomeFrameworkProtocol{// ...}class MyContainer<Element>:MyContainerSuperclass,MyContainerProtocol,SomeoneElsesContainerProtocol,SomeFrameworkContainerProtocol{// ...}class MyContainer<BaseCollection>:MyContainerSuperclass,MyContainerProtocol,SomeoneElsesContainerProtocol,SomeFrameworkContainerProtocolwhere BaseCollection: Collection {// ...}class MyContainer<BaseCollection>:MyContainerSuperclass,MyContainerProtocol,SomeoneElsesContainerProtocol,SomeFrameworkContainerProtocolwhereBaseCollection: Collection,BaseCollection.Element: Equatable,BaseCollection.Element: SomeOtherProtocolOnlyUsedToForceLineWrapping{// ...}
函数调用
当一个函数断句换行后,每一个参数都声明在独立的一行中,跟原始行有 2 个空格缩进。和函数声明一样,函数调用结尾的右括号(没有尾闭包)既可以在参数行的末尾,也可以单独起一行。
✅let index = index(of: veryLongElementVariableName,in: aCollectionOfElementsThatAlsoHappensToHaveALongName)let index = index(of: veryLongElementVariableName,in: aCollectionOfElementsThatAlsoHappensToHaveALongName)
如果函数调用时有尾闭包,那么尾闭包的参数必须换行后单独一行,并将参数列表用括号括起来,以便与下面的闭包区分开来。
✅someAsynchronousAction.execute(withDelay: howManySeconds, context: actionContext) {(context, completion) indoSomething(withContext: context)completion()}
控制流声明
当一个控制流的声明需要断句换行(比如 if、guard、 while、 for),继续的下一行和前面保持一样的缩进。如果语法上是嵌套,前面加 2 个空格缩进。结尾的花括号依然可以接着末尾或单独起一行。对于 guard 要强调的是 else { 必须写在同一行。
✅if aBooleanValueReturnedByAVeryLongOptionalThing() &&aDifferentBooleanValueReturnedByAVeryLongOptionalThing() &&yetAnotherBooleanValueThatContributesToTheWrapping() {doSomething()}if aBooleanValueReturnedByAVeryLongOptionalThing() &&aDifferentBooleanValueReturnedByAVeryLongOptionalThing() &&yetAnotherBooleanValueThatContributesToTheWrapping(){doSomething()}if let value = aValueReturnedByAVeryLongOptionalThing(),let value2 = aDifferentValueReturnedByAVeryLongOptionalThing() {doSomething()}if let value = aValueReturnedByAVeryLongOptionalThing(),let value2 = aDifferentValueReturnedByAVeryLongOptionalThingThatForcesTheBraceToBeWrapped(){doSomething()}guard let value = aValueReturnedByAVeryLongOptionalThing(),let value2 = aDifferentValueReturnedByAVeryLongOptionalThing() else {doSomething()}guard let value = aValueReturnedByAVeryLongOptionalThing(),let value2 = aDifferentValueReturnedByAVeryLongOptionalThing()else {doSomething()}for element in collectionwhere element.happensToHaveAVeryLongPropertyNameThatYouNeedToCheck {doSomething()}
其他表达式
如果是表达式过长需要换行,下一行前面加 2 个缩进。如果换成多行,每一行都比前一行增加 2 个缩进。
✅let result = anExpression + thatIsMadeUpOf * aLargeNumber +ofTerms / andTherefore % mustBeWrapped + (andWeWill - keepMakingItLonger * soThatWeHave / aContrivedExample)
下面的缩进多了:
❌let result = anExpression + thatIsMadeUpOf * aLargeNumber +ofTerms / andTherefore % mustBeWrapped + (andWeWill - keepMakingItLonger * soThatWeHave / aContrivedExample)
过长的表达式还是建议重构代码,拆分为几个变量再做运算会好一点。
