1.命名
1.1 类型
- 类型名称(如
struct,enum,class,typedef,associatedtype,protocol等)使用大驼峰命名法命名 - 变量和常量则以小驼峰命名法命名
enum Planet {
case mercury, venus, earth, mars, jupiter, saturn, uranus,neptune
}
struct Person {
var mobileNo: String
}
1.2 Protocol
根据苹果接口设计指导准则,协议名称用来描述一些东西是什么的时候是名词,例如:Collection, WidgetFactory。若协议名称用来描述能力应该以 -ing, -able, 或 -ible 结尾,例如:Equatable, Resizing。
1.3 类前缀
Swift使用模块名作为 namespace,在使用了Podfile的工程中,主工程、PodA、PodB…PodN 各自在自己的Module中,不会发生模块间同名文件/同名变量的命名冲突。因此在Swift中可以不使用前缀,这点跟OC有区别。
1.4 方法
如果返回Bool值,则命名上应该体现出判断的意思,尽量带上should/would/will/is/can等词语
func shouldCancel(request: Request) -> Bool {
}
1.5 委托
- 在自定义Delegate的方法命名时使用代理源作为第一个未命名参数。未命名参数指使用_标记的参数。为什么要默认添加代理源?为了方便协议实现方在Delegate方法中判断本次回调来源于哪个source
推荐:
func namePickerView(_ namePickerView: NamePickerView, didSelectName name: String)
func namePickerViewShouldReload(_ namePickerView: NamePickerView) -> Bool
不推荐:
func didSelectName(namePicker: NamePickerViewController, name: String)
func namePickerShouldReload() -> Bool
- 当使用委托和协议时,请注意避免出现循环引用,基本上是在定义属性的时候使用 weak 修饰。
- 当一个对象要实现协议一致性时,推荐使用 extension 隔离协议中的方法集,这样让相关方法和协议集中显示在一起,也简化了类支持一个协议和实现相关方法的流程
推荐:
class MyViewcontroller: UIViewController {
// 方法
}
extension MyViewcontroller: UITableViewDataSource {
// UITableViewDataSource 方法
}
extension MyViewcontroller: UIScrollViewDelegate {
// UIScrollViewDelegate 方法
}
不推荐:
class MyViewcontroller: UIViewController, UITableViewDataSource, UIScrollViewDelegate {
// 所有的方法
}
1.6 泛型
泛型变量命名应该语义清晰并且使用首字母大写驼峰命名。如果实在无法明确语义,则优先使用T、U、V这几个大家普遍约定的缩写,方便代码阅读者推断其为泛型类型
推荐:
struct Stack<Element>{ ... }
func writeTo <Target: OutputStream>(to Target: inout Target)
func swap(_ a: inout T, _ b: inout T)
不推荐:
struct Stack<T>{ ... }
func write<target: OutputStream> (to target: inout target)
func swap<Thing>(_ a: inout Thing, _ b: inout Thing)
2.代码逻辑
2.1 self推断
- 让编译器在所有允许的地方推断
self。 - 在 init 中设置参数时显性地使用 self 。
- 在 non-escaping closures 中显性地使用 self 。
- 在避免命名冲突时使用 self 。
例:
class BoardLocation {
let row: Int, column: Int
init(row: Int, column: Int) {
self.row = row
self.column = column
let closure = {
print(self.row)
}
}
}
2.2 常量
- 类常量应该在类型里声明为 static。使用 static 修饰常量可以允许他们在被引用的时候不需要实例化类型。
- 除了单例以外,应尽量避免生成全局常量。
- 定义常量使用 let 关键字,定义变量使用 var 关键字, 如果变量的值未来不会发生变化要使用常量。
例:
struct PhysicsModel {
static var speedOfLightInAVacuum = 299_792_458
}
class Spaceship {
static let topSpeed = PhysicsModel.speedOfLightInAVacuum
var speed: Double
func fullSpeedAhead() {
speed = Spaceship.topSpeed
}
}
2.3 计算型类型属性
- 当只需要继承 getter 方法时,返回简单的 Computed 属性即可。
正确:
class Example {
var age: UInt32 {
return arc4random()
}
}
错误:
class Example {
var age: UInt32 {
get {
return arc4random()
}
}
}
- 如果你在属性中添加了 set 或者 didSet ,那么你应该显示地提供 get 方法。
class Person {
var age: Int {
get {
return Int(arc4random())
}
set {
print("That's not your age.")
}
}
}
2.4 单例
Swift 中单例很简单,Swift 的 runtime 会保证单例的创建并且采用线程安全的方式访问:
class ControversyManager {
static let sharedInstance = ControversyManager()
private init() {}
}
单例通常只需要访问 sharedInstance 的静态属性,除非你有不得已的原因去重命名它。注意,不要用静态函数或者全局函数去访问你的单例。
2.5 错误处理
可以使用 do/try/catch 机制,避免使用 try! 和 try?
2.6 可选值
- 声明一个函数的某个参数可以为 nil 时,用?
- 当你确定某个变量在使用时已经确定不是 nil 时,在后面加 !
2.7 数据类型
优先使用Swift 数据类型,实在实现不了才使用Objective-C的数据类型
推荐:
let width = 120.0 // Double
let widthString = "\(width)" // String
不推荐,因为需要类型转化:
let width = 120.0 // Double
let widthString = (width as NSNumber).stringValue // String
2.8 类型推断
变量声明时更推荐让编译器做类型推断;但空集合类型需要显式写明类型
推荐:
let message = "Click the button"
let currentBounds = computeViewBounds()
var names = ["Mic", "Sam", "Christine"]
let maximumWidth: CGFloat = 106.5
var names: [String] = []
var lookup: [String: Int] = [:]
不推荐:
let message: String = "Click the button"
let currentBounds: CGRect = computeViewBounds()
var names = [String]()
var lookup = [String: Int]()
2.9 流程控制
2.9.1 优先使用for-in、forEach的swift风格循环控制,而不是while的C风格
推荐:
for _ in 0..<3 {
print("Hello three times")
}
for (index, person) in attendeeList.enumerated() {
print("\(person) is at position #\(index)")
}
for index in stride(from: 0, to: items.count, by: 2) {
print(index)
}
for index in (0...3).reversed() {
print(index)
}
不推荐:
var i = 0
while i < 3 {
print("Hello three times")
i += 1
}
var i = 0
while i < attendeeList.count {
let person = attendeeList[i]
print("\(person) is at position #\(i)")
i += 1
}
2.9.2 三元操作符?:
建议只用于做简单的赋值操作,不要三元组嵌套三元组,否则会降低代码可读性;有复杂判断时应使用if判断拆分逻辑
推荐:
let value = 5
result = (value != 0 ? x : y)
let isHorizontal = true
result = (isHorizontal ? x : y)
不推荐:
result = a > b ? x = c > d ? c : d : y
2.9.3 if等嵌套层级
- 必要时要使用guard代替if降低嵌套层级
推荐:
func computeFFT(context: Context?, inputData: InputData?) throws -> Frequencies {
guard let context = context else {
throw FFTError.noContext
}
guard let inputData = inputData else {
throw FFTError.noInputData
}
// use context and input to compute the frequencies
return frequencies
}
不推荐:
func computeFFT(context: Context?, inputData: InputData?) throws -> Frequencies {
if let context = context {
if let inputData = inputData {
// use context and input to compute the frequencies
return frequencies
} else {
throw FFTError.noInputData
}
} else {
throw FFTError.noContext
}
}
- 当有多个条件需要用 guard 或 if let 解包,可用复合语句避免嵌套。
guard let number1=number1, number2=number2, number3=number3 else{
fatalError("impossible")
}
// 处理number
2.9.4 Switch 模块中不用显式使用break
3. 代码结构
3.1 左大括号不用另起一行:
class SomeClass {
func someMethod() {
if x == y {
/* ... */
} else if x == z {
/* ... */
} else {
/* ... */
}
}
/* ... */
}
3.2 空格
3.2.1 逗号后面加空格
let myArray = [1, 2, 3, 4, 5]
3.2.2 运算符的前后添加空格
let myValue = 20 + (30 / 2) * 3
if 1 + 1 == 3 {
fatalError("The universe is broken.")
}
func pancake() -> Pancake {
/* ... */
}
3.2.3 类型声明时:紧跟属性,与类型之间加空格
推荐:
let width = 120.0
let height: Double = 120.0
var title: String
不推荐:
let width=1.0
let height:Double = 1.0
var title:String
3.2.4 方法与方法之间或类与类、枚举、结构体之间空一行,不可留置不必要的多空行
3.4 当调用的函数有多个参数时,每一个参数另起一行,并比函数名多一个缩进。
someFunctionWithManyArguments(
firstArgument: "Hello, I am a string",
secondArgument: resultFromSomeFunction()
thirdArgument: someOtherLocalVariable)
3.5 当遇到需要处理的数组或字典内容较多需要多行显示时,需把 [ 和 ] 类似于方法体里的括号,方法体里的闭包也要做类似处理
someFunctionWithABunchOfArguments(
someStringArgument: "hello I am a string",
someArrayArgument: [
"dadada daaaa daaaa dadada daaaa daaaa dadada daaaa daaaa",
"string one is crazy - what is it thinking?"
],
someDictionaryArgument: [
"dictionary key 1": "some value 1, but also some more text here",
"dictionary key 2": "some value 2"
],
someClosure: { parameter1 in
print(parameter1)
})
3.6 在写枚举类型的时候,尽量简写。
// 推荐
imageView.setImageWithURL(url, type: .person)
// 不推荐
imageView.setImageWithURL(url, type: AsyncImageView.Type.person)
3.7 默认的访问控制修饰符是 internal, 如果需要使用internal 可以省略不写
3.8 注释
- 使用XCode自带的注释功能
- 使用
//MARK:做代码分隔