为了可读性在一些声明里可以把一行过长的代码分成几行。比如下面的情况,如果生硬的写在一行里读起来就会很困难:
❌
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?
where
Elements.Element == Element,
Element: Equatable
{ // 注意花括号换行了
for current in elements {
// ...
}
}
如果花括号换行没有换行,函数体视觉上就会容易和声明混在一起:
❌
public func index<Elements: Collection, Element>(
of element: Element,
in collection: Elements
) -> Elements.Index?
where
Elements.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
) -> ( // 返回值是一个复杂的 tuple
Element.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,
SomeFrameworkContainerProtocol
where BaseCollection: Collection {
// ...
}
class MyContainer<BaseCollection>:
MyContainerSuperclass,
MyContainerProtocol,
SomeoneElsesContainerProtocol,
SomeFrameworkContainerProtocol
where
BaseCollection: 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) in
doSomething(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 collection
where 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)
过长的表达式还是建议重构代码,拆分为几个变量再做运算会好一点。