投稿文章,作者:南栀傾寒(博客)
最近工作也比較穩定了,公司在做代碼規范和組件化的跳轉。 鑒於使用Objective-C的開發成員比較多, 我們架構師就整理了Objective-C的代碼規范。不過作為Swift開發的老司機,也整理了一套適用與Swift的代碼規范。 以後可以直接拿來用了。
注釋
// 單行注釋 /* 多行注釋 */ /// 標記注釋1 /** 標記注釋2 */
建議使用VVDocument-Xode插件
文檔注釋
以/* ..../ 標記, 不用再沒一行開頭都加*號,支持markdown書寫,例如:
/** ## Feature Support This class does some awesome things. It supports: - Feature 1 - Feature 2 - Feature 3 ## Examples Here is an example use case indented by four spaces because that indicates a code block: let myAwesomeThing = MyAwesomeClass() myAwesomeThing.makeMoney() ## Warnings There are some things you should be careful of: 1. Thing one 2. Thing two 3. Thing three */ class MyAwesomeClass { /* ... */ }
方法注釋
用 - parameter注釋來標記參數等
/** This does something with a `UIViewController`, perchance. - warning: Make sure that `someValue` is `true` before running this function. */ func myFunction() { /* ... */ }
命名
使用可讀的駝峰命名法去給類 方法 變量 命名。 class struct protocol enum 應使用大寫,變量名使用小寫
private let maximumWidgetCount = 100 class WidgetContainer { var widgetButton: UIButton let widgetHeightPercentage = 0.85 }
對於全局函數,init方法 ,建議每個參數都使用外部變量,來保證可讀性
func dateFromString(dateString: String) -> NSDate func convertPointAt(column column: Int, row: Int) -> CGPoint func timedAction(afterDelay delay: NSTimeInterval, perform action: SKAction) -> SKAction! // would be called like this: dateFromString("2014-03-14") convertPointAt(column: 42, row: 13) timedAction(afterDelay: 1.0, perform: someOtherAction)
Protocols 協議命名
建議遵守Apple's API DesignGuidelines使用名詞來描述,如 ing able ible,例如
Collection WidgerFactory Equtable Resizing
Emumerations 枚舉命名規范
使用首字母小寫的駝峰命名法給每個case命名
enum Shape { case rectangle case square case rightTriangle case equilateralTriangle }
Class Prefixes類型前綴
官方建議不使用前綴,因為swift有命名空間的概念
但是由於在項目開發中不可避免使用開源庫,大部分使用pods管理,但是有時候需要針對需要定制功能,直接修改源碼,這時候是直接將源碼放在工程中,而且大部分的項目都是混編項目。可能導致命名沖突,此處還建議用LJ(Lianjia)當作命名前綴
class LJHomeViewController: UIViewController {}
Selector選擇器
建議使用可推測的上下文環境,來創建選擇器,而不是點擊Xcode的Fix it ,這樣會產生一個全名稱 選擇器
let sel = #selector(viewDidLoad)
不推薦
let sel = #selector(ViewController.viewDidLoad)
Generics泛型
泛型命名應該使用大寫的駝峰命名法,,如果給一個泛型起名字其實沒意義,可以使用常見的T,U,V來命名
推薦
struct Stack{ ... } func writeTo(inout target: Target) func max(x: T, _ y: T) -> T
不推薦
struct Stack{ ... }//命名無意義 func writeTo(inout t: target)// 首字母未大寫 func max(x: Thing, _ y: Thing) -> Thing//簡稱即可
Code Formatting 代碼格式
留空白
建議使用tabs 而不是使用空格
文件結束時留一行空白
用足夠的空行把代碼分割為合理的邏輯塊,而不是非常緊湊
不要在一行代碼結尾處留空格
更不要在空行(\n)中使用縮進(\t)
聲明類型時,將冒號與標識符連在一起
當聲明一個變量時冒號緊跟變量,空一格再寫類型
class SmallBatchSustainableFairtrade: Coffee { ... } let timeToCoffee: NSTimeInterval = 2 func makeCoffee(type: CoffeeType) -> Coffee { ... }
Control Flow 控制流
建議使用Swift范的for in 循環而不是 while or c 式for循環
for _ in 0..<3 { print("Hello three times") } for (index, person) in attendeeList.enumerate() { print("\(person) is at position #\(index)") } for index in 0.stride(to: items.count, by: 2) { print(index) } for index in (0...3).reverse() { //3,2,1,0 print(index) }
代碼塊縮進
(if/else/switch/while etc.)或者method function 的大括號留在當前行,並前保留一個空格 ,能省略的不要添加
如
if user.isHappy { // Do something } else { // Do something else }
不推薦
if (user.isHappy ) 多余空格 { 換行位置不對 // Do something } else { // Do something else }
Early Return
當你遇到某些操作需要條件判斷去執行,應該使用防御式編程 盡早返回
如
guard n.isNumber else { return } guard let number1 = number1, number2 = number2, number3 = number3 else { fatalError("impossible") } // do something with numbers // Use n here //guard 理解為確保的意思, 如 確保n是一個數字 不推薦使用if判斷 if n.isNumber { // Use n here } else { return } 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") }
Semicolons 分號
不要寫分號,不要寫分號,不要寫分號 Swift不同於JavaScript ,詳情參看:
generally considered unsafe---Do you recommend using semicolons after every statement in JavaScript?
更不建議把多句代碼塊放在一行中
自定義運算符的時候左右盡量各保留一個空格
如
func <|(lhs: rhs:="" -=""> Int func <|<(lhs: A, rhs: A) -> A // 重構後 func <| lhs:="" rhs:="" -=""> Int func <|< (lhs: A, rhs: A) -> A
代碼分割
使用良好的代碼分割讓你的代碼塊更具有邏輯性
// MARK: - 類似@parma mark - // MARK: 類似@parma mark
ProtocolConformance 協議保持一致性
一個類型實現一個協議時建議單獨聲明一個擴展,保證邏輯性分離
如
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 }
無用的代碼要刪除
無用的代碼和注釋要刪除 ,避免給閱讀代碼的人造成困惑和疑問
類型定義
盡可能的使用swift自帶類型,在必須的時候才做橋接 ,String-> NSString , Set->NSSet
更多的使用let,而不是var
盡量let foo = something 而非 var for = somthing
let-有保障 並且它的值的永遠不會改變,對同事也是個 清晰的標記,對於它的用法,之後的代碼可以做個強而有力的推斷。更容易明白代碼的含義。否則的話一旦你用了 var,還要去考慮值會不會改變,這時候你就不得不人肉去檢查。
這樣,無論何時你看到 var,就假設它會變,並找到原因。
常量
不建議直接命名頂級變量,建議定義在結構體或者枚舉內部,用static let 聲明。 可以給這些變量一個合適的命名空間
enum Math { static let e = 2.718281828459045235360287 static let pi = 3.141592653589793238462643 } radius * Math.pi * 2 // circumference
Optional可選類型
盡量不要使用強制解包
對於一個可選類型var foo = Type? 不要使用強制解包
foo!.doSomethind()
使用可選綁定,或者 可選鏈操作
if let foo = foo { // Use unwrapped `foo` value in here } else { // If appropriate, handle the case where the optional is nil } //或者 // Call the function if `foo` is not nil. If `foo` is nil, ignore we ever tried to make the call foo?.callSomethingIfFooIsNotNil()
避免使用隱式可選類型
如果 foo 可能為 nil ,盡可能的用 let foo: FooType? 代替 let foo: FooType!(注意:一般情況下,?可以代替!)
Struct Initializers 結構體初始化
使用結構體初始化而不是CGGet。。。之類的創建方法
let bounds = CGRect(x: 40, y: 20, width: 120, height: 80) let centerPoint = CGPoint(x: 96, y: 42)
Lazy Initialization
對於較大開銷的初始化或者配置較多的初始化建議放在加載屬性裡
lazy var locationManager: CLLocationManager = self.makeLocationManager() private func makeLocationManager() -> CLLocationManager { let manager = CLLocationManager() manager.desiredAccuracy = kCLLocationAccuracyBest manager.delegate = self manager.requestAlwaysAuthorization() return manager }
Classes and Structures 結構體和類
首選struct而非class
在非必需(比如沒有生命周期)的時候使用struct,因為多態可以使用protocl實現 繼承可以使用組合實現
值類型容易辨別,更可以用let去推測不可變的行為
只有在必須時才使用self
忘掉Objective-C到底時使用self.pro 還是_ivar的訪問方式,對於swift內部調用properties或者method省略掉self
private class History { var events: [Event] func rewrite() { events = [] } }
只有在使用閉包或者命名沖突時再加上self
extension History { init(events: [Event]) { self.events = events } var whenVictorious: () -> () { return { self.rewrite() } } }
只有在使用閉包時self 增強了被捕獲的語義,其它時候是冗余的
對於只讀的屬性或者下標語法,使用隱式的getter方法
建議
var myGreatProperty: Int { return 4 } subscript(index: Int) -> T { return objects[index] }
不建議完整的寫法,比較繁瑣
var myGreatProperty: Int { get { return 4 } } subscript(index: Int) -> T { get { return objects[index] } }
請把class默認標記為final
組合通常比繼承更合適,而且不用 繼承意味著考慮的更加健壯
// Turn any generic type into a reference type using this Box class. final class Box{ let value: T init(_ value: T) { self.value = value } }
類型推斷
能讓系統推斷的類型不要顯示指明
如
struct Composite{ func compose(other: Composite) -> Composite{ return Composite(self, other) } } let num:Int = 4
重構為
struct Composite{ func compose(other: Composite) -> Composite { return Composite(self, other) } } let num = 4
空的字典和空數組的類型 使用類型標記 加強語義
var names: [String] = [] var lookup: [String: Int] = [:]
函數聲明
函數名要簡短清晰,如果能保持在一行內,大括號也要保持在一行,如果不能換行並用Tab\b縮進
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.animateWithDuration(1.0) { self.myView.alpha = 0 } UIView.animateWithDuration(1.0, animations: { self.myView.alpha = 0 }, completion: { finished in self.myView.removeFromSuperview() } )
常見的閉包語義可以使用其縮略形式
let value = numbers.map { $0 * 2 }.filter { $0 % 3 == 0 }.indexOf(90) let value = numbers .map {$0 * 2} .filter {$0 > 50} .map {$0 + 10}
Syntactic Sugar語法糖
對於有語法糖的建議使用,提升可讀性
如
var deviceModels: [String] var employees: [Int: String] var faxNumber: Int?
而不是
var deviceModels: Array var employees: Dictionary var faxNumber: Optional
內存管理
對於class類型需要注意內存管理
普通的閉包建議使用[weak self] 或者[unowned self] 對於異步的閉包建議使用 [weak self] and guard let strongSelf = self else { return }搭配使用
weak 避免出現循環引用, strongself 避免在異步回調中 捕獲列表中捕獲的變量被析構
resource.request().onComplete { [weak self] response in guard let strongSelf = self else { return } let model = strongSelf.updateModel(response) strongSelf.updateUI(model) }
對於頂級類型,函數,變量定義,明確的列出權限控制
對於全局變量 頂級函數,類型,永遠應該有著詳盡的權限控制說明符
public var whoopsGlobalState: Int internal struct TheFez {} private func doTheThings(things: [Thing]) {}
參考自
Github
Prolificinterative
Raywenderlich