Swift Coding Style
2016-03-28 本文已影响171人
zjs_dev
本篇文章介绍如何用最规范的形式书写Swift。作者力求团队内部合作的时候代码风格统一,减少模糊代码,尽量美观,保持最佳实践。
基本
- 缩进使用Tab而不是Space
- 文件结尾添加一行空行
- 行尾不建议使用『;』
- 正括号不换行,与之前的字母空一个空格
if user.isHappy {
// Do something
} else {
// Do something else
}
- 『:』,『,』左边不空格,右边空一个
- 『[』,『]』,『(』,『)』内部不建议空格
let a: [String] = ["1", "b"]
- 单行注释『//』后面空一格
- 使用『// MARK:』,『// TODO:』,『// FIXME:』来注释
// MARK: 添加键盘方法
// MARK: - UITableViewDelegate (带横线版本会被编译成水平分割线)
// TODO: zjs, 多线程保护
// FIXME: 缺少类型判断
- 不使用namespace前缀(NSXXX,RRDXXX),swift会根据framework自动添加namespace
进阶
尽量使用let代替var
Preferred:
let tempValue = "tempValue"
Not Preferred:
var temp = "tempValue"
推荐使用类型推断
Preferred:
let message = "Click the button"
let currentBounds = computeViewBounds()
var names = [String]()
let maximumWidth: CGFloat = 106.5
Not Preferred:
let message: String = "Click the button"
let currentBounds: CGRect = computeViewBounds()
var names: [String] = []
推荐使用便捷语法
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>
尽量使用String代替NSString
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
全局变量命名使用UpperCamelCase
Preferred:
public let MaximumWidgetCount = 100
Not Preferred:
public let maximumWidgetCount = 100
public let MAX_WIDGET_COUNT = 100
枚举类型命名使用UpperCamelCase
enum Shape {
case Rectangle
case Square
case Triangle
case Circle
}
尽早的使用guard判断退出
Preferred:
func login(name: String?, password: String?) {
guard let name = name, password = password else {return}
// ...do something
}
Not Preferred:
func login(name: String?, password: String?) {
// ...do something
if let name = name, password = password {
// ...do something
} else {return}
}
尽量使用as?代替as!
Preferred:
func parse(dict: [String: AnyObject]) {
guard let data = dict["data"] as? [String: AnyObject] else {return}
// ...do something
}
Not Preferred:
func parse(dict: [String: AnyObject]) {
let data = dict["data"] as! [String: AnyObject]
// ...do something
}
尽量使用Optional Chaining
Preferred:
func pop(viewController: UIViewController?) {
viewController?.navigationController?.popViewControllerAnimated(true)
}
Not Preferred:
func pop(viewController: UIViewController?) {
if let viewController = viewController {
// ...do something
}
}
read-only properties 推荐使用隐式的get方法
Preferred:
var myGreatProperty: Int {
return 4
}
Not Preferred:
var myGreatProperty: Int {
get {
return 4
}
}
对外使用的类显示标注public和private
public var whoopsGlobalState: Int
private func doTheThings(things: [Thing]) {}
private(set) var offset:Int = 0;
访问类变量时不省略self
Preferred:
class History {
var events: [Event] = [Event]()
func rewrite() {
self.events = []
}
}
Not Preferred:
class History {
var events: [Event]
func rewrite() {
events = []
}
}
括号表达式
Preferred:
UIView.animateWithDuration(1.0) {
self.myView.alpha = 0
}
UIView.animateWithDuration(1.0,
animations: {
self.myView.alpha = 0
},
completion: { finished in
self.myView.removeFromSuperview()
}
)
Not Preferred:
UIView.animateWithDuration(1.0, animations: {
self.myView.alpha = 0
})
UIView.animateWithDuration(1.0,
animations: {
self.myView.alpha = 0
}) { f in
self.myView.removeFromSuperview()
}
Struct初始化
Preferred:
let bounds = CGRect(x: 40, y: 20, width: 120, height: 80)
let centerPoint = CGPoint(x: 96, y: 42)
Not Preferred:
let bounds = CGRectMake(40, 20, 120, 80)
let centerPoint = CGPointMake(96, 42)
不需要时尽量省略参数信息
Preferred:
struct Composite<T> {
…
func compose(other: Composite) -> Composite {
return Composite(self, other)
}
}
Not Preferred:
struct Composite<T> {
…
func compose(other: Composite<T>) -> Composite<T> {
return Composite<T>(self, other)
}
}
自定义运算符加空格
Preferred:
func <| (lhs: Int, rhs: Int) -> Int
func <|< <A>(lhs: A, rhs: A) -> A
Not Preferred:
func <|(lhs: Int, rhs: Int) -> Int
func <|<<A>(lhs: A, rhs: A) -> A
最佳实践
单例
class Account {
static let sharedInstance = Account()
}
推荐使用extension实现protocol
Prefrerred:
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
}
Not Preferred:
class MyViewcontroller: UIViewController, UITableViewDataSource, UIScrollViewDelegate {
// all methods
}
尽量使用Struct而不是Class
使用Struct的情况:
- 赋值操作默认是copy操作
- 类型不属于AnyObject,属于Any
- 使用protocol+extension代替继承
protocol Vehicle {
var numberOfWheels: Int { get }
}
func maximumTotalTirePressure(vehicle: Vehicle, pressurePerWheel: Float) -> Float {
return pressurePerWheel * Float(vehicle.numberOfWheels)
}
struct Bicycle: Vehicle {
let numberOfWheels = 2
}
struct Car: Vehicle {
let numberOfWheels = 4
}
使用Class的情况:
- 赋值操作是指针复制操作
- 属于AnyObject
- 尽量使用protocol+extension代替继承
class默认添加final关键字
非基础类声明默认添加final关键字,如果后面有人需要继承再删除。
final class History {
var events: [Event] = [Event]()
}
lazy写法
final class MyViewController: UIViewController {
lazy var mainView: UIView = {
let view = UIView()
... // do somethig
return view
}()
}
block weakself
func myFuction() {
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, Int64(2 * Double(NSEC_PER_SEC))), dispatch_get_main_queue(), {[weak self] () -> Void in
guard let `self` = self else {return}
self.backgroundColor = UIColor.blackColor()
}
Json解析
if let JSONObject = try NSJSONSerialization.JSONObjectWithData(data, options: .AllowFragments) as? [[String: AnyObject]],
let username = (JSONObject[0]["user"] as? [String: AnyObject])?["name"] as? String {
// There's our username
}
lazy sequence
lazy方式使用map,flatmap方法。
使用lazy方式每次访问都会进行计算。
let array = Array(1...1000)
let mapArray = array.lazy.map {$0 + 1}
print(mapArray[2]) // 访问时执行map运算
参考资料
https://github.com/raywenderlich/swift-style-guide
https://github.com/github/swift-style-guide
http://nshipster.cn/swift-documentation/
http://swift.gg/2016/03/25/being-lazy/