官方 raywenderlich.com Swift 编程风格指南
本文版权归 raywenderlich.com 、The Official raywenderlich.com Swift Style Guide项目以及全部贡献者全部。
译者翻译仅供知识传播使用。
本风格指南的目标是让Swift代码更简洁、可读更强。
语言
推荐使用跟苹果API文档风格统一的英语。
推荐:
var color = "red"
不推荐:
var colour = "red"
空白
- 使用2个空白符作为缩进,不要使用
Tab
制表符。请务必在Xcode
中进行设置; - 方法定义时的大括号或其他大括号(
if
/else
/switch
/while
等)中的左括号写在当前语句的结尾。右括号须要另起一行。
推荐:
if user.isHappy { //Do something } else { //Do something else }
if user.isHappy { //Do something } else { //Do something else }
- 方法定义之间须要插入一个空行。让代码在视觉上更清晰,也便于管理。方法内部也须要使用空行来分隔不同功能的代码块。但假设一个方法中有太多代码块,你最好将它们拆分为多个方法(重构)。
凝视
仅仅有在必要的时候才写凝视来解释某段代码为什么那么做。凝视必须跟代码同步更新,该删掉时就删掉。
尽量避免使用多行凝视,而是让代码本身去解释自己的功能。
命名
使用可描写叙述性的驼峰式命名法为类、函数、方法、变量等命名。
模块中的类名和常量名首字母须要大写,而方法名和变量名的首字母须要小写。
推荐:
let MaximumWidgetCount = 100 class WidgetContainer { var widgetButton: UIButton let widgetHeightPercentage = 0.85 }
不推荐:
let MAX_WIDGET_COUNT = 100 class app_widgetContainer { var wBut: UIButton let wHeightPct = 0.85 }
对于函数和构造器(init
),除非能一目了然。否则建议为每一个參数命名。
假设能让函数可读性更强。请提供每一个參数的外部參数名。
func dateFromString(dateString: NSString) -> NSDate func convertPointAt(#column: Int, #row: Int) -> CGPoint func timedAction(#delay: NSTimeInterval, perform action: SKAction) -> SKAction! // would be called like this: dateFromString("2014-03-14") convertPointAt(column: 42, row: 13) timedAction(delay: 1.0, perform: someOtherAction)
对于类中的方法。请遵循苹果惯例,将方法名作为第一个參数的外部名:
class Guideline { func combineWithString(incoming: String, options: Dictionary?) { ... } func upvoteBy(amount: Int) { ... } }
假设在非代码文本(包括教程、书籍以及凝视中)中引用了某个函数。请提供函数全部參数的外部參数名:
The dateFromString() function is great. Call convertPointAt(column:, row:) from your init() method. The return value of timedAction(delay:, perform:) may be nil. Guideline objects only have two methods: combineWithString(options:) and upvoteBy() You shouldn't call the data source method tableView(cellForRowAtIndexPath:) directly.
类前缀
Swift中的模块(module
)包括了命名空间。全部在模块中定义的类型名都不会与其他模块冲突。所以我们不再须要使用前缀命名来降低命名冲突。假设两个来自不同模块的同样名字须要同一时候引用。你能够使用模块名+点+类型名的方式来处理:
import MyModule var myClass = MyModule.MyClass()
再次重申,请不要在Swift类型命名时加前缀。
假设你须要将Swift类型暴露给Objective-C,你能够为其指定一个在Objective-C
中使用的合适前缀(请參考ObjC编程风格指南:
@objc (RWTChicken) class Chicken { ... }
分号
Swift不须要在每条语句后加分号。但假设将多条语句写在一行代码中,这时须要加上分号。
然而我们并不推荐这样的将多行语句写在一行的做法。 唯一例外是for-conditional-increment
结构,它必须使用分号。但你应该尽量使用for-in
结构来替代这样的行为: 推荐:
var swift = "not a scripting language"
不推荐:
var swift = "not a scripting language";
注意: Swift跟Javascript有很大差别,Javascript中假设忽略分号会被觉得造成代码的不安全。
类和结构体
请将类和结构体中的代码按下面顺序进行组织:
- 变量和常量属性
- 构造器
- 公共方法
- 私有方法
class Circle: Shape { var x: Int, y: Int var radius: Double var diameter: Double { get { return radius * 2 } set { radius = newValue / 2 } } init(x: Int, y: Int, radius: Double) { self.x = x self.y = y self.radius = radius } convenience init(x: Int, y: Int, diameter: Double) { self.init(x: x, y: y, radius: diameter / 2) } func describe() -> String { return "I am a circle at ((x),(y)) with an area of (computeArea())" } func computeArea() -> Double { return M_PI * radius * radius } }
上面的样例还展示了下面风格:
- 定义属性、变量、常量、參数以及其他语句时。在其后面加上空格,而不是在前面加。比方说,
x: Int
和Circle: Shape
; - 属性的
getter
、setter
以及属性观察器willSet
和didSet
的实现都须要缩进; - 假设多个变量和结构体有同样的使用目的或使用环境,能够将它们定义在同一行代码中。
Self 的使用
请避免在Swift中使用self
,由于我们不须要使用self
来訪问一个对象的属性或调用它的方法。
唯一须要使用的场景是在类或结构体的构造器中。你能够使用self
来区分传入的參数和类/结构体的属性:
class BoardLocation { let row: Int, column: Int init(row: Int,column: Int) { self.row = row self.column = column } }
函数定义
尽量将较短的函数名定义在一行,并以一个左大括号结尾:
func reticulateSplines(spline: Double[]) -> Bool { // reticulate code goes here }
假设函数名较长,请在适当的时候换行,并对下一行函数名进行缩进:
func reticulateSplines(spline: Double[], adjustmentFactor: Double, translateConstant: Int, comment: String) -> Bool { // reticulate code goes here }
闭包
请尽量使用掉尾
(就是将最后一个闭包參数直接附在方法调用后,看起来像是控制语句的body一样)闭包语法。不管何时,请给闭包中每一个參数一个描写叙述性的名字:
return SKAction.customActionWithDuration(effect.duration) { node, elapsedTime in // more code goes here }
笔者:@aemaeth 提出将闭包參数另起一行的写法,我觉得更合理,攻克了多个闭包參数过长的问题。
对于一行表达式闭包,当使用场景明白时。能够使用隐式返回:
attendeeList.sort { a, b in a > b }
类型
请尽量使用Swift提供的原生类型。
Swift也提供了原生类型对象桥接到Objective-C
对象的办法。所以必要时你能任意使用这些桥接对象提供的方法。
推荐:
let width = 120.0 //Double let widthString = width.bridgeToObjectiveC().stringValue //String
不推荐:
let width: NSNumber = 120.0 //NSNumber let widthString: NSString = width.stringValue //NSString
在Sprite Kit
代码中,请多使用CGFloat
,这样代码会更简洁,也能避免许多的类型转换。
常量
常量使用let
keyword定义,而变量使用var
keyword定义。
假设一个值是常量。那就必须使用let
keyword来准确定义。
终于你会发现。你使用let
的频率远大于var
。
小技巧:一開始你能够将全部值都定义为常量,然后假设编译器报错了再作适当的调整。
可选
假设能够接受nil
值。请将变量或函数返回值的类型定义为可选类型(加?
)。
当你明白知道实例变量在使用前会完毕初始化,比方说视图控制器中的子视图subviews
在使用前会在viewDidLoad
中初始化,那么你能够将这些变量定义为隐式解析类型(使用!
)。
当訪问一个可选值时。假设仅仅訪问一次,或者方式时有多种可能性,请使用可选链:
myOptional?.anotherOne?.
optionalView?.setNeedsDisplay()
使用可选绑定optional binding
对仅仅拆包(unwrap
)一次,但运行多次操作的情况很合适:
if let view = self.optionalView { // do many things with view }
类型判断
Swift编译器能够判断出变量和常量的类型。你能够为每一个常量或变量提供一个显示的类型(加个冒号,并在后面写上类型名),但大部分情况不必这么做。
我们建议多使用类型判断让编译器自己主动判断出常量或变量的类型,这样代码会更紧凑。
推荐:
let message = "Click the button" var currentBounds = computeViewBounds()
不推荐:
let message: String = "Click the button" var currentBounds: CGRect = computeViewBounds()
注意:遵循此规则意味着在命名时,须要更谨慎的选择具有描写叙述性的名字。
流程控制
建议多使用for-in
风格的for
循环,而不是传统的for-condition-increment
风格。
推荐:
for _ in 0..5 { println("Hello five times") } for person in attendeeList { // do something }
不推荐:
for var i = 0; i < 5; i++ { println("Hello five times") } for var i = 0; i < attendeeList.count; i++ { let person = attendeeList[i] // do something }
笑脸
笑脸在raywenderlich.com站点中是一个很突出的特性。正确的使用笑脸来表达在编程时的一种极大的快乐和兴奋,至关重要。我们使用右方括号]
,由于它代表了ASCII
艺术字符中最大的微笑。而使用右小括号)
的笑脸显得有点不那么诚心,所以我们不推荐使用。
推荐:
:]
不推荐:
:)