类型/Types
Always use Swift's native types and expressions when available. Swift offers bridging to Objective-C so you can still use the full set of methods as needed.
请尽可能多的使用 Swift 原生类型。 Swift 提供了 Objective-C 桥接,所以当你需要时仍然可以使用对应的方法。
推荐(Preferred):
let width = 120.0 // Double
let widthString = "\(width)" // String
可接受(Less Preferred):
let width = 120.0 // Double
let widthString = (width as NSNumber).stringValue // String
不推荐(Not Preferred):
let width: NSNumber = 120.0 // NSNumber
let widthString: NSString = width.stringValue // NSString
In drawing code, use CGFloat
if it makes the code more succinct by avoiding too many conversions.
在绘图相关的代码中,使用 CGFloat
可以避免代码频繁的转换,从而让你的代码更加简洁。
常量/Constants
Constants are defined using the let
keyword and variables with the var
keyword. Always use let
instead of var
if the value of the variable will not change.
使用 let
关键字来定义常量,使用 var
关键字来定义变量。如果变量的值不会改变,则要使用 let
来代替 var
。
Tip: A good technique is to define everything using let
and only change it to var
if the compiler complains!
提示: 一个比较好的技巧是所有东西都使用 let
定义, 当编译器警告时再改为 var
。
You can define constants on a type rather than on an instance of that type using type properties. To declare a type property as a constant simply use static let
. Type properties declared in this way are generally preferred over global constants because they are easier to distinguish from instance properties. Example:
你可以在类型上定义常量,而不是在实例上通过属性来定义常量。使用 static let
把一个类型属性声明为常量。相比于全局变量声明常量的方式,更推荐用这种方式,因为这种方式更能和实例属性区分开。举例:
推荐(Preferred):
enum Math {
static let e = 2.718281828459045235360287
static let root2 = 1.41421356237309504880168872
}
let hypotenuse = side * Math.root2
Note: The advantage of using a case-less enumeration is that it can't accidentally be instantiated and works as a pure namespace.
注意: 使用无枚举值的枚举类型时,它的一大优势就是不会被意外的实例化,只是一个单纯的命名空间。
不推荐(Not Preferred):
let e = 2.718281828459045235360287 // pollutes global namespace
let root2 = 1.41421356237309504880168872
let hypotenuse = side * root2 // what is root2?
静态方法和可变类型属性/Static Methods and Variable Type Properties
Static methods and type properties work similarly to global functions and global variables and should be used sparingly. They are useful when functionality is scoped to a particular type or when Objective-C interoperability is required.
静态方法和类型属性跟全局函数和全局变量的工作原理类似,应当谨慎使用。当功能的作用域是一个特定类型,或需要与 Objective-C 交互时,它们非常有用。
可选类型/Optionals
Declare variables and function return types as optional with ?
where a nil
value is acceptable.
在可接受 nil
值的情况下,使用 ?
声明变量和函数返回类型为可选类型。
Use implicitly unwrapped types declared with !
only for instance variables that you know will be initialized later before use, such as subviews that will be set up in viewDidLoad()
. Prefer optional binding to implicitly unwrapped optionals in most other cases.
用 !
声明的隐式解包类型,适用于在使用前一定能被正确初始化的实例变量,比如在 viewDidLoad
中设置子视图。大多数场景下,倾向使用可选绑定而不是隐式解包。
When accessing an optional value, use optional chaining if the value is only accessed once or if there are many optionals in the chain:
当访问一个可选值时,如果可选值仅被访问一次,或在调用链中有许多可选值时,使用可选链:
textContainer?.textLabel?.setNeedsDisplay()
Use optional binding when it's more convenient to unwrap once and perform multiple operations:
当可选值只需一次就可解绑,且之后执行的操作与该可选值相关,使用可选绑定:
if let textContainer = textContainer {
// do many things with textContainer
}
Notes: Swift 5.7 introduced new shorthand syntax for unwrapping optionals into shadowed variables:
注意: Swift 5.7 引入了一种新的简写语法,用于将可选项解包到阴影变量(shadowed variables)中:
if let textContainer {
// do many things with textContainer
}
When naming optional variables and properties, avoid naming them like optionalString
or maybeView
since their optional-ness is already in the type declaration.
在命名可选变量和属性时,避免类似 optionalString
或 maybeView
这样的命名,因为它们的可选性已经体现在类型声明中。
For optional binding, shadow the original name whenever possible rather than using names like unwrappedView
or actualLabel
.
对于可选绑定,适当时使用原始名称,而不是使用像 unwrappedView
或 actualLabel
这样的名称。
推荐(Preferred):
var subview: UIView?
var volume: Double?
// later on...
if let subview = subview, let volume = volume {
// do something with unwrapped subview and volume
}
// another example
resource.request().onComplete { [weak self] response in
guard let self = self else { return }
let model = self.updateModel(response)
self.updateUI(model)
}
不推荐(Not Preferred):
var optionalSubview: UIView?
var volume: Double?
if let unwrappedSubview = optionalSubview {
if let realVolume = volume {
// do something with unwrappedSubview and realVolume
}
}
// another example
UIView.animate(withDuration: 2.0) { [weak self] in
guard let strongSelf = self else { return }
strongSelf.alpha = 1.0
}
延迟初始化/Lazy Initialization
Consider using lazy initialization for finer grained control over object lifetime. This is especially true for UIViewController
that loads views lazily. You can either use a closure that is immediately called { }()
or call a private factory method. Example:
在更细粒度地控制对象声明周期时,可以考虑使用延迟初始化。对于 UIViewController
,延迟初始化视图是非常正确的。你可以使用立即调用的闭包(比如 { }()
)或调用私有工厂方法。例如:
lazy var locationManager = makeLocationManager()
private func makeLocationManager() -> CLLocationManager {
let manager = CLLocationManager()
manager.desiredAccuracy = kCLLocationAccuracyBest
manager.delegate = self
manager.requestAlwaysAuthorization()
return manager
}
注意/Notes:
[unowned self]
is not required here. A retain cycle is not created.因为没有发生循环引用,所以这里不需要
[unowned self]
。Location manager has a side-effect for popping up UI to ask the user for permission so fine grain control makes sense here.
位置管理器向用户申请权限时,有弹出 UI 界面的副作用,所以细颗粒地控制在这里是有意义的。
类型推断/Type Inference
Prefer compact code and let the compiler infer the type for constants or variables of single instances. Type inference is also appropriate for small, non-empty arrays and dictionaries. When required, specify the specific type such as CGFloat
or Int16
.
优先选择简洁紧凑的代码,让编译器对单个实例的常量或变量进行推断类型。类型推断也适合于小型的,非空的数组和字典。当数据是一些特殊类型,如 CGFloat
或 Int16
时,请指明此特定类型。
推荐(Preferred):
let message = "Click the button"
let currentBounds = computeViewBounds()
var names = ["Mic", "Sam", "Christine"]
let maximumWidth: CGFloat = 106.5
不推荐(Not Preferred):
let message: String = "Click the button"
let currentBounds: CGRect = computeViewBounds()
var names = [String]()
空数组和空字典的类型注释/Type Annotation for Empty Arrays and Dictionaries
For empty arrays and dictionaries, use type annotation. (For an array or dictionary assigned to a large, multi-line literal, use type annotation.)
为空数组和空字典使用类型注释。(对于内容量大、多行的字面量数组和字典可以使用类型注释。)
推荐(Preferred):
var names: [String] = []
var lookup: [String: Int] = [:]
不推荐(Not Preferred):
var names = [String]()
var lookup = [String: Int]()
NOTE: Following this guideline means picking descriptive names is even more important than before.
注意:遵循此原则意味着在命名时要更注重命名的描述性。
语法糖/Syntactic Sugar
Prefer the shortcut versions of type declarations over the full generics syntax.
推荐使用简短版本的类型声明,而不是完整的泛型语法。
推荐(Preferred):
var deviceModels: [String]
var employees: [Int: String]
var faxNumber: Int?
不推荐(Not Preferred):
var deviceModels: Array<String>
var employees: Dictionary<Int, String>
var faxNumber: Optional<Int>