这是一个Swift开发代码规范git
尽量地让你的代码在没有警告的状况下编译。这个规则适用不少的状况,好比#selector
类型去替代适用字符串字面值。github
命名遵循如下关键点:swift
(img) 当你在这个列表里参阅的时候,清晰的含义就显得格外重要了。你须要让别人尽量简单的去参阅一个方法名。数组
addTarget
Swift里面的类型已经自动地增长了它们所属模块的命名空间,你不须要为它们编写前缀。若是来自不一样模块的两个名称发生冲突,您能够经过使用模块名称预先肯定类型名称来消除歧义。可是,只有在可能出现混淆的状况下才指定模块名称。(可是在tdw项目里面,仍是建议使用前缀,这个前缀是用来做为tdw出品的标识)xcode
import SomeModule
let myClass = MyModule.UsefulClass()
复制代码
当你自定义一个代理方法等时候,第一个参数应该是一个匿名参数,这个参数是代理源。(UIkit包含了不少这样的例子) 正确的:安全
func namePickerView(_ namePickerView: NamePickerView, didSelectName name: String)
func namePickerViewShouldReload(_ namePickerView: NamePickerView) -> Bool
复制代码
错误的:bash
func namePickerView(_ namePickerView: NamePickerView, didSelectName name: String)
func namePickerViewShouldReload(_ namePickerView: NamePickerView) -> Bool
复制代码
利用编译器上下文类型推断编写更短更简洁的代码。闭包
正确的:app
let selector = #selector(viewDidLoad)
view.backgroundColor = .red
let toView = context.view(forKey: .to)
let view = UIView(frame: .zero)
复制代码
错误的ide
let selector = #selector(ViewController.viewDidLoad)
view.backgroundColor = UIColor.red
let toView = context.view(forKey: UITransitionContextViewKey.to)
let view = UIView(frame: CGRect.zero)
复制代码
范型类型应该被有意义的描述,用驼峰命名首字母大写的方式编写。若是你找不到词语用来表达这个类型的意思,可使用传统的26个字母中的某一个,大写,来表示,好比:T,U,V等。 正确的:
struct Stack<Element> { ... }
func write<Target: OutputStream>(to target: inout Target)
func swap<T>(_ 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)
复制代码
语言
使用美式英语来描述API 正确的:
let color = "red"
复制代码
错误的:
let colour = "red"
复制代码
使用extensions来组织你的代码逻辑块。每一个extension都应该使用// MARK: -
来讲明该extension的功能。
特别的,当你增长一个继承协议的时候,最好用extension分割协议方法。
正确的:
class MyViewController: UIViewController {
// class stuff here
}
// MARK: - UITableViewDataSource
extension MyViewController: UITableViewDataSource {
// table view data source methods
}
// MARK: - UIScrollViewDelegate
extension MyViewController: UIScrollViewDelegate {
// scroll view delegate methods
}
复制代码
错误的:
class MyViewController: UIViewController, UITableViewDataSource, UIScrollViewDelegate {
// all methods
}
复制代码
没有用的代码,该删掉的仍是要删掉,不要只是注释掉
xcode设置:
控制流
正确的:
if user.isHappy {
// Do something
} else {
// Do something else
}
复制代码
错误的:
if user.isHappy
{
// Do something
}
else {
// Do something else
}
复制代码
关于空格的使用:
正确的:
class TestDatabase: Database {
var data: [String: CGFloat] = ["A": 1.2, "B": 3.2]
}
复制代码
错误的:
class TestDatabase: Database {
var data: [String: CGFloat] = ["A": 1.2, "B": 3.2]
}
复制代码
当须要时,使用注释来解释为何特定的代码会作一些事情。注释必须保持最新或删除。
记住,struct具备值语义。对于没有身份的事物使用结构。包含a、b、c的数组与包含a、b、c的另外一个数组彻底相同,它们是彻底可互换的。无论你用的是第一个数组仍是第二个数组,由于它们表示的是彻底同样的东西。这就是为何数组是结构的缘由。
类具备引用语义。为具备标识或特定生命周期的事物使用类。你能够将一我的建模为一个类,由于两个对象是两个不一样的东西。仅仅由于两我的有相同的名字和生日,并不意味着他们是同一我的。可是这我的的生日是一个结构体,由于1950年3月3日的日期和1950年3月3日的任何其余日期对象是同样的。日期自己没有标识。
下面是一个精心设计的类定义示例:
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)
}
override func area() -> Double {
return Double.pi * radius * radius
}
}
extension Circle: CustomStringConvertible {
var description: String {
return "center = \(centerString) area = \(area())"
}
private var centerString: String {
return "(\(x),\(y))"
}
}
复制代码
上面的例子遵循如下的设计指南:
为了简洁起见,当不须要访问对象的属性或调用它的方法时要避免使用‘self’。
只有当编译器要求必须使用self的时候才使用。
为了简洁起见,若是一个计算属性是只读的,则省略get子句。只有在提供了set子句时,才须要get子句。
正确的:
var diameter: Double {
return radius * 2
}
复制代码
错误的:
var diameter: Double {
get {
return radius * 2
}
}
复制代码
不容许对其修饰的内容进行继承或者从新操做。
final标记class或members可能会分散主题,这没必要需。然而,使用 final 有时能够阐明你的意图,是值得的。在如下示例中, Box 有一个特殊用途而且并不打算容许在派生类定制。标记 final 会让这很清楚。
// Turn any generic type into a reference type using this Box class.
final class Box<T> {
let value: T
init(_ value: T) {
self.value = value
}
}
复制代码
在一行上使用简洁的功能声明(包括大括号)
func reticulateSplines(spline: [Double]) -> Bool {
// reticulate code goes here
}
对于具备长签名的功能, 在适当的点添加换行符,并后续行上添加一个额外的缩进
func reticulateSplines(spline: [Double], adjustmentFactor: Double,
translateConstant: Int, comment: String) -> Bool {
// reticulate code goes here
}
复制代码
仅在参数列表末尾有单个闭包表达式参数时才使用尾随闭包。并给出闭包参数的描述性名称。
正确的:
UIView.animate(withDuration: 1.0) {
self.myView.alpha = 0
}
UIView.animate(withDuration: 1.0, animations: {
self.myView.alpha = 0
}, completion: { finished in
self.myView.removeFromSuperview()
})
复制代码
错误的:
UIView.animate(withDuration: 1.0, animations: {
self.myView.alpha = 0
})
UIView.animate(withDuration: 1.0, animations: {
self.myView.alpha = 0
}) { f in
self.myView.removeFromSuperview()
}
复制代码
对于上下文清晰的简单闭包表达式,能够用隐式返回:
attendeeList.sort { a, b in
a > b
}
复制代码
使用尾随闭包的连接方法在上下文中应该清晰易读。关于间距,换行,何时使用命名和匿名的参数,这些都留给做者自行判断。
let value = numbers.map { $0 * 2 }.filter { $0 % 3 == 0 }.index(of: 90)
let value = numbers
.map {$0 * 2}
.filter {$0 > 50}
.map {$0 + 10}
复制代码
尽量使用Swift自带的类型。Swift为Objective-C提供了桥接文件,当你须要时你仍然可使用它全部的方法。
正确的:
let width = 120.0 // Double
let widthString = (width as NSNumber).stringValue // String
复制代码
错误的:
let width: NSNumber = 120.0 // NSNumber
let widthString: NSString = width.stringValue // NSString
复制代码
定义常量使用 let 关键字, 变量使用 var 关键字。若是变量的值不会改变一般使用 let,而不是 var 。
提示:有一个好方法就是全部东西都使用 let 定义,编译器报警告时再把它改成 var
您能够在类型上定义常量,而不是使用类型属性定义该类型的实例 。使用 static let 声明一个仅仅做为常数的类型属性。用这种方式声明的类型属性一般优于全局常量,由于他们更容易从实例属性中区分。
正确的:
enum Math {
static let e = 2.718281828459045235360287
static let root2 = 1.41421356237309504880168872
}
let hypotenuse = side * Math.root2
注解:使用这种枚举的优势是它不会被不当心实例化或者只是一个单纯的命名空间
复制代码
错误的:
let e = 2.718281828459045235360287 // pollutes global namespace
let root2 = 1.41421356237309504880168872
let hypotenuse = side * root2 // what is root2?
复制代码
静态方法和类型属性的工做方式相似于全局函数和全局变量,应该谨慎使用。当功能局限于某一特定类型或者要求与Objective-C互操做时是颇有用的。
将变量和函数返回类型声明为可选类型,返回一个nil值是可接受的。
只有在实例变量使用前肯定会初始化的前提下,才能使用 !隐式强解的类型声明,例如将设置的子视图viewDidLoad。
访问可选值时,若是该值仅访问一次,或者链中有多个可选项,请使用可选连接:
self.textContainer?.textLabel?.setNeedsDisplay()
复制代码
使用可选绑定更方便一次打开并执行多个操做:
if let textContainer = self.textContainer {
// do many things with textContainer
}
复制代码
当命名可选变量和属性时,避免像optionalString或maybeView这样命名它们,由于它们的可选参数已经在类型声明中。
对于可选绑定适当隐藏原名,而不是使用unwrappedView或actualLabel这样的名称。
正确的:
var subview: UIView?
var volume: Double?
// later on...
if let subview = subview, let volume = volume {
// do something with unwrapped subview and volume
}
复制代码
错误的:
var optionalSubview: UIView?
var volume: Double?
if let unwrappedSubview = optionalSubview {
if let realVolume = volume {
// do something with unwrappedSubview and realVolume
}
}
复制代码
考虑使用懒加载细粒度控制对象生命周期。对于UIViewController那些加载视图而言,这一点尤为如此。您可使用当即调用的闭包 ()或调用私有工厂方法。
lazy var locationManager: CLLocationManager = self.makeLocationManager()
private func makeLocationManager() -> CLLocationManager {
let manager = CLLocationManager()
manager.desiredAccuracy = kCLLocationAccuracyBest
manager.delegate = self
manager.requestAlwaysAuthorization()
return manager
}
复制代码
注释: [unowned self]这里不须要 没有建立保留循环。 位置管理器具备弹出UI的反作用,要求用户得到许可,所以细粒度控制在这里是有意义的。
但愿使用简洁的代码,让编译器推断单个实例的常量或变量的类型。类型推断也适用于小(非空)数组和字典。须要时,指定特定的类型如CGFloat或Int16。
正确的:
let message = “点击按钮”
let currentBounds = computeViewBounds()
var names = [ “ Mic ”,“ Sam ”,“ Christine ” ]
let maximumWidth : CGFloat = 106.5
复制代码
错误的:
let message : String = “点击按钮”
let currentBounds : CGRect = computeViewBounds()
let names = [ String ]()
复制代码
空数组和字典的类型注释: 对于空数组和字典,使用类型注释。(对于分配了大量多行内容的数组或字典,使用类型标注)
正确的:
var names: [String] = []
var lookup: [String: Int] = [:]
复制代码
错误的:
var names = [String]()
var lookup = [String: Int]()
复制代码
注意:遵循此准则意味着选择描述性名称比之前更重要。
与完整的泛型语法相比,更倾向于使用简洁版的类型声明。
正确的:
var deviceModels: [String]
var employees: [Int: String]
var faxNumber: Int?
复制代码
错误的:
var deviceModels: Array<String>
var employees: Dictionary<Int, String>
var faxNumber: Optional<Int>
复制代码
对于不附加到类或类型的不受约束的功能,应该谨慎使用。若是能够,尽量使用‘.’方法而不是不受约束的功能。这有助于可读性和可发现性。 正确的:
let sorted = items.mergeSorted() // easily discoverable
rocket.launch() // acts on the model
复制代码
错误的:
let sorted = mergeSort(items) // hard to discover
launch(&rocket)
复制代码
自由功能异常:
let tuples = zip(a, b) // feels natural as a free function (symmetry)
let value = max(x, y, z) // another free function that feels natural
复制代码
代码不该该建立引用循环,即便是非生产或教程demo中。分析您的对象图,并使用weak或unowned防止强烈的循环。或者,使用值类型(struct,enum)来彻底防止循环。
使用【weak self】和 guard let strongSelf = self else { return }语句扩展对象生命周期。【weak self】相对于【unowned self】的优点不是特别明显时,不使用self。明显延长使用周期,显然比可选展开更好。
推荐的:
resource.request().onComplete { [weak self] response in
guard let strongSelf = self else {
'' return
}
let model = strongSelf.updateModel(response)
strongSelf.updateUI(model)
}
复制代码
不推荐的:
// might crash if self is released before response returns
resource.request().onComplete { [unowned self] response in
let model = self.updateModel(response)
self.updateUI(model)
}
复制代码
不推荐的:
// deallocate could happen between updating the model and updating UI
resource.request().onComplete { [weak self] response in
let model = self?.updateModel(response)
self?.updateUI(model)
}
复制代码
文章中的全局访问控制注释可能会分散主题,这而且不是必要的。然而,适当地使用private和fileprivate,增进了清晰度并促进了封装。条件容许的话,尽量使用private而不是fileprivate 。使用扩展可能须要您使用fileprivate。
只有当你须要完整的访问控制规范时才明确的使用open,public及internal。
使用访问控制做为首选属性说明符。访问控制前惟一的条件是 static符,或属性例如@IBAction、@IBOutlet和@discardableResult等。
推荐的:
private let message = "Great Scott!"
class TimeMachine {
fileprivate dynamic lazy var fluxCapacitor = FluxCapacitor()
}
复制代码
不推荐的:
fileprivate let message = "Great Scott!"
class TimeMachine {
lazy dynamic fileprivate var fluxCapacitor = FluxCapacitor()
}
复制代码
尽可能使用for-in风格的for循环而不是while-condition-increment风格。
推荐的:
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
}
复制代码
当用条件语句编码时,左边的代码应该是"golden" 或 "happy”路径。也就是说,不要嵌套if语句。使用多个返回语句也是能够的,这个guard就是为此创建的。
推荐的:
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,
let number2 = number2,
let number3 = number3 else {
fatalError("impossible")
}
// do something with numbers
复制代码
不推荐的:
if let number1 = number1 {
if let number2 = number2 {
if let number3 = number3 {
// do something with numbers
} else {
fatalError("impossible")
}
} else {
fatalError("impossible")
}
} else {
fatalError("impossible")
}
复制代码
Guard声明须要以某种方式退出。通常状况下,这应该是简单的一行语句,如return、throw、break、continue和fatalError()。大量代码的block应该避免使用。若是有多个退出点须要清除代码,请考虑使用defer(延迟)代码块来避免重复清除代码。
Swift在您的任何一个代码语句后都不须要分号。若是你想在一行中组合多个语句,则须要‘;’。 但不建议在一行上写用‘;’分隔的多条语句。
推荐的:
let swift = "not a scripting language"
复制代码
不推荐的:
let swift = "not a scripting language";
复制代码
注意:Swift与JavaScript大相径庭,后者省略分号一般被认为是不安全的。
if 附近的括号不是必须的,应该省略。 推荐的:
if name == "Hello" {
print("World")
}
复制代码
不推荐的:
if (name == "Hello") {
print("World")
}
复制代码
在有大量代码的表达式中,有括号可使代码更清晰地读取。
推荐的:
let playerMark = (player == current ? "X" : "O")
复制代码
凡是xcode有关的,机构应该设置为‘Ray Wenderlich’ ,Bundle Identifier应该设置为com.razeware.TutorialName形式,其中TutorialName是项目的名字。