Swift编程规范

1. 目录

[TOC]

2. 修订信息

版本 时间 修订人 修订内容
1.0 2016-03-28 麦志泉 创建文档

3. 概述

为帮助开发者实现代码的优美与一致性、可读性和简洁性,
本人编写了Swift编程规范指南,包含命名、空格、注释、类和结构体、
函数声明、闭包表达式、类型、控制流、分号等。


4. 规范指南

4.1 命名(Naming)

使用驼峰式的描述性命名方式,为类,方法,变量等命名。类名的首字母应该大写,而方法和变量的首字母使用小写字符。

推荐做法:

private let maximumWidgetCount = 100
class WidgetContainer {
  var widgetButton: UIButton
  let widgetHeightPercentage = 0.85
}

不推荐做法:

let MAX_WIDGET_COUNT = 100
class app_widgetContainer {
  var wBut: UIButton
  let wHeightPct = 0.85
}

对于函数和初始化方法,推荐对所有的参数进行有意义的命名,
除非上下文已经非常清楚。如果外部参数命名可以使得函数调用更加可读,
也应该把外部参数命名包含在内。

func dateFromString(dateString: String) -> NSDate
func convertPointAt(#column: Int, #row: Int) -> CGPoint
func timedAction(#delay: NSTimeInterval, perform action: SKAction) -> SKAction!
// 调用方式如下:
dateFromString("2014-03-14")
convertPointAt(column: 42, row: 13)
timedAction(delay: 1.0, perform: someOtherAction)

对于方法来说,参照标准的苹果惯例,方法命名含义要引用到第一个参数:

class Guideline {
  func combineWithString(incoming: String, options: Dictionary?) { ... }
  func upvoteBy(amount: Int) { ... }
}

4.2 枚举(Enumerations)

使用首字母大写的驼峰命名规则来命名枚举值:

enum Shape {
  case Rectangle
  case Square
  case Triangle
  case Circle
}

4.3 文章(Prose)

当我们在文章中(教程,图书,注释等)需要引用到函数时,需要从调用者的视角考虑,包含必要的参数命名,或者使用_表示不需要命名的参数。

从你自身实现的init中调用convertPointAt(column:row:)。
如果你调用dateFromString(_:),需要保证你提供的输入字符串格式是”yyyy-MM-dd”。
如果你需要在viewDidLoad()中调用timedAction(delay:perform:),记得提供调整后的延迟值和需要处理的动作。

你不能直接调用数据源方法tableView(_:cellForRowAtIndexPath:)
当你遇到疑问时,可以看看Xcode在jump bar中是如何列出方法名的 —— 我们的风格与此匹配。

image

4.4 类的前缀(Class Prefixes)

Swift类型自动被模块名设置了名称空间,所以你不需要加一个类的前缀。如果两个来自不同模块的命名冲突了,你可以附加一个模块名到类型命名的前面来消除冲突。

import SomeModule
let myClass = MyModule.UsefulClass()

4.5 空格(Spacing)

使用Xcode默认配置的数字就是

  • 方法定义的大括号或者其他大括号(if/else/switch/while等)- 一般都放在定义名称的同一行,
    并且使用一个新的行来结束。
  • 提示:你可以通过以下方法重新进行缩进:选择一些代码(或者使用⌘A选择所有),然后按Control-I(或者点击菜单栏 Editor\Structure\Re-Indent)。一些Xcode模板代码使用的缩进是4个空格,所以这种方法可以很好的修复缩进。

推荐做法:

if user.isHappy {
    // Do something
} else {
    // Do something else
}

4.6 注释(Comments)

枚举注释:

/**
 证件印

 - MainLand:  中国大陆
 - HKorMacao: 香港澳门地区
 - Taiwan:    台湾
 - Oversea:   海外
 */
enum IdcardAreaType: String {
    case MainLand = "1"
    case HKorMacao = "2"
    case Taiwan = "3"
    case Oversea = "4"
}

方法注释:

/**
 向下取第几位小数

 - parameter places: 第几位小数 ,1

 15.96 * 10.0 = 159.6
 floor(159.6) = 159.0
 159.0 / 10.0 = 15.9

 - returns:  15.96 =  15.9
 */
func f(places:Int) -> Double {
    let divisor = pow(10.0, Double(places))
    return floor(self * divisor) / divisor
}
}

4.7 定义的案例(Example definition)

以下是一个风格很好的类定义:

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)  
  }  
  func describe() -> String {  
    return "I am a circle at \(centerString()) with an area of \(computeArea())"  
  }  
  override func computeArea() -> Double {  
    return M_PI * radius * radius  
  }  
  private func centerString() -> String {  
    return "(\(x),\(y))"  
  }  
}  

以上例子遵循了以下风格规范:

  • 指定属性、变量、常量、参数定义或者其他定义的类型,在冒号后面,紧跟着一个空格,而不是把空格放在冒号前面。比如:x: Int和Circle: Shape。
  • 如果能表示相同的目的和上下文,可以在同一行定义多个变量和结构体。
  • 缩进getter,setter的定义和属性观察器的定义。
  • 不需要添加internal这样的默认的修饰符。同样的,不需要在重写一个方法时添加访问修饰符。

4.8 Self的使用(Use of Self)

为了保持简洁,避免使用 self 关键词,Swift 不需要使用 self 来访问对象属性和调用对象方法。

必须使用 self 来区分构造器中属性命名和参数命名,还有在闭包表达式中引用属性值(编译器需要区分):

class BoardLocation {  
  let row: Int, column: Int  
  init(row: Int, column: Int) {  
    self.row = row  
    self.column = column  
    let closure = {  
      println(self.row)  
    }  
  }  
}

4.9 协议遵守(Protocol Conformance)

当我们对一个类添加协议时,推荐使用一个单独的类扩展来实现协议的方法。这可以保持协议相关的方法聚合在一起,同时也可以简单的标识出一个协议对应类中需要实现哪些对应的方法。

同时,别忘了添加// MARK:,注释可以使得代码组织的更好!

推荐做法:

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  
}

4.9 计算属性(Computed Properties)

为了保持简洁,如果一个计算属性是只读的,请忽略掉get语句。只有在需要定义set语句的时候,才提供get语句。

推荐做法:

var diameter: Double {  
  return radius * 2  
}  

不推荐做法:

var diameter: Double {  
  get {  
    return radius * 2  
  }  
}

4.10 函数声明(Function Declarations)

保证短的函数定义在同一行中,并且包含左大括号:

func reticulateSplines(spline: [Double]) -> Bool {  
  // reticulate code goes here  
}  

在一个长的函数定义时,在适当的地方进行换行,同时在下一行中添加一个额外的缩进:

func reticulateSplines(spline: [Double], adjustmentFactor: Double,  
    translateConstant: Int, comment: String) -> Bool {  
  // reticulate code goes here  
}  

4.11 闭包表达式(Closure Expressions)

如果闭包表达式参数在参数列表中的最后一个时,使用尾部闭包表达式。给定闭包参数一个描述性的命名。

推荐做法:

UIView.animateWithDuration(1.0) {  
  self.myView.alpha = 0  
}  
UIView.animateWithDuration(1.0,  
  animations: {  
    self.myView.alpha = 0  
  },  
  completion: { finished in  
    self.myView.removeFromSuperview()  
  }  
)  

不推荐做法:

UIView.animateWithDuration(1.0, animations: {  
  self.myView.alpha = 0  
})  
UIView.animateWithDuration(1.0,  
  animations: {  
    self.myView.alpha = 0  
  }) { f in  
    self.myView.removeFromSuperview()  
}  

当单个闭包表达式上下文清晰时,使用隐式的返回值:

attendeeList.sort { a, b in  
  a > b  
}  

4.12 类型(Types)

尽可能使用 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

在Sprite Kit代码中,使用CGFloat可以使得代码更加简明,避免很多转换。

4.13 常量(Constants)

常量定义使用 let 关键字,变量定义使用 var 关键字,如果变量的值不需要改变,请尽量使用 let 关键字。

提示:一个好的技巧是,使用 let 定义任何东西,只有在编译器告诉我们值需要改变的时候才改成 var 定义。

全局或类的成员常量命名前加k

//MARK: - 常量
let kMarketChartDataRefreshTime: NSTimeInterval = 60      //数据刷新间隔
let kMarketDataRefreshTime: NSTimeInterval = 4      //数据刷新间隔

4.14 可选类型(Optionals)

当nil值是可以接受的时候时,定义变量和函数返回值为可选类型(?)。

当你确认变量在使用前已经被初始化时,使用!来显式的拆包类型,比如在viewDidLoad中会初始化subviews。

当你访问一个可选值时,如果只需要访问一次或者在可选值链中有多个可选值时,请使用可选值链:

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, 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  
  }  
}

4.15 结构体构造器(Struct Initializers)

使用原生的 Swift 结构体构造器,比老式的几何类(CGGeometry)的构造器要好。

推荐做法:

let bounds = CGRect(x: 40, y: 20, width: 120, height: 80)  
let centerPoint = CGPoint(x: 96, y: 42)  

不推荐做法:

let bounds = CGRectMake(40, 20, 120, 80)  
let centerPoint = CGPointMake(96, 42)  

推荐使用结构体限定的常量CGRect.infiniteRect,CGRect.nullRect等,来替代全局常量CGRectInfinite,CGRectNull等。对于已经存在的变量,可以直接简写成 .zeroRect。

4.15 类型推断(Type Inference)

推荐使用更加紧凑的代码,让编译器能够推断出常量和变量的类型。除非你需要定义一个特定的类型(比如CGFloat和Int16),而不是默认的类型。

推荐做法:

let message = "Click the button"  
let currentBounds = computeViewBounds()  
var names = [String]()  
let maximumWidth: CGFloat = 106.5  

不推荐做法:

let message: String = "Click the button"  
let currentBounds: CGRect = computeViewBounds()  
var names: [String] = []  

注意: 遵守这条规则意味选择描述性命名比之前变得更加重要。

4.16 语法糖(Syntactic Sugar)

推荐使用类型定义简洁的版本,而不是全称通用语法。

推荐做法:

var deviceModels: [String]  
var employees: [Int: String]  
var faxNumber: Int?  

不推荐做法:

var deviceModels: Array<String>  
var employees: Dictionary<Int, String>  
var faxNumber: Optional<Int>  

4.17 控制流(Control Flow)

推荐循环使用for-in表达式,而不使用for-condition-increment表达式。

推荐做法:

for _ in 0..<3 {  
  println("Hello three times")  
}  
for (index, person) in enumerate(attendeeList) {  
  println("\(person) is at position #\(index)")  
}  

不推荐做法:

for var i = 0; i < 3; i++ {  
  println("Hello three times")  
}  
for var i = 0; i < attendeeList.count; i++ {  
  let person = attendeeList[i]  
  println("\(person) is at position #\(i)")  
}  

4.17 �类成员变量声明

苹果UIKit框架中的UIButton, UILabel等组件类,声明最好以组件名开头,这样在代码提示中容易被检索归类

推荐做法:

/// MARK: - 成员变量
@IBOutlet var tableViewWealth: UITableView!
@IBOutlet var labelTotalAssetsTitle: UILabel!
@IBOutlet var labelTotalAssets: UILabel!

当类使用到代理协议的设计模式时,delegate前要加weak

weak var delegate: BankListViewDelegate?
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,332评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,508评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,812评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,607评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,728评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,919评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,071评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,802评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,256评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,576评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,712评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,389评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,032评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,798评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,026评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,473评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,606评论 2 350

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,637评论 18 139
  • importUIKit classViewController:UITabBarController{ enumD...
    明哥_Young阅读 3,784评论 1 10
  • 86.复合 Cases 共享相同代码块的多个switch 分支 分支可以合并, 写在分支后用逗号分开。如果任何模式...
    无沣阅读 1,354评论 1 5
  • Spring Boot 参考指南 介绍 转载自:https://www.gitbook.com/book/qbgb...
    毛宇鹏阅读 46,778评论 6 342
  • 1晕轮效应 在爱情的世界里,最美好的就是我喜欢你而你也喜欢着我。作为一名单身的男性看到漂亮的姑娘总是...
    杜小才阅读 845评论 0 0