博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Swift Code Style
阅读量:5913 次
发布时间:2019-06-19

本文共 12827 字,大约阅读时间需要 42 分钟。

前言

本文档是翻译著名raywenderlich Swift Style Guide 并结合自身情况结合生成

内容目录

  •   *   *   *   *   *
  •   *   *
  •   *   *   *
  •   *   *   *   *   *   *

基本准则

将警告视为错误(健壮的工程应该没有一个警告)

命名

使用驼峰命名法命名类, 方法, 变量等等, 类名以及协议名首字母应大写, 然而方法名和变量名首字母小写.常量应以 k 作为首字母

推荐 :

private let kMaximumWidgetCount = 100class WidgetContainer {  var widgetButton: UIButton  let widgetHeightPercentage = 0.85}复制代码

不推荐 :

let MAX_WIDGET_COUNT = 100class app_widgetContainer {  var wBut: UIButton  let wHeightPct = 0.85}复制代码

通常应该避免缩略语和首字母缩略词, 缩写词和缩写应统一大写或小写. 例子:

推荐

let urlString: URLStringlet userID: UserID复制代码

不推荐

let uRLString: UrlStringlet userId: UserId复制代码

对于函数以及 init 方法, 倾向于对所有的参数命名, 除非上下文非常简洁清晰.如果能使函数更具可读性, 我们也可以包括外部参数名称.

func dateFromString(dateString: String) -> NSDatefunc convertPointAt(column column: Int, row: Int) -> CGPointfunc timedAction(afterDelay delay: NSTimeInterval, perform action: SKAction) -> SKAction!// 调用方式:dateFromString("2014-03-14")convertPointAt(column: 42, row: 13)timedAction(afterDelay: 1.0, perform: someOtherAction)复制代码

常量和变量

1.公开变量:用 Public 修饰, 如果这个变量是必传参数, 应该用 ! 标明, 如果是可选参数, 用 ? 标明  2.私有变量:用 private 修饰.

枚举

根据Swift3苹果代码规范,枚举名称首字母应该大写,枚举值使用小驼峰. 例如:

enum Shape {  case rectangle  case square  case rightTriangle  case equilateralTriangle}复制代码

OC命名同使用大写驼峰命名法, 而枚举值必须以枚举名开头, 便于与 Swift 混编

typedef NS_ENUM(NSUInteger, OKIShape) {  case OKIShareRectangle  case OKIShareSquare  case OKIShareTriangle  case OKIShareCircle}复制代码

类前缀

按理说Swift不应该加入类前缀,但是为了和OC规范一致,在此项目类前缀为 OK + 项目名称的首字母

泛型

泛型类型参数应具有描述性,大写驼峰型的名字. 在类型名称时,没有一个有意义的关系或角色,请使用传统的单一的大写字母,例如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复制代码

使用国际英语而不是美式英语

推荐:

let color = "red"复制代码

不推荐:

let colour = "red"复制代码

代码结构

使用扩展将代码组织成逻辑功能的块. 每个扩展应设置一个注释:// MARK: - 保持良好的注释.

协议一致性

在 Swift 中, 推荐使用 extension 的方式来分割实现的代码, 在视觉上可以更好的加强用户的阅读体验, 当然, 别忘了加上 //MARK: -

推荐:

class MyViewcontroller: UIViewController {  // class stuff here}// MARK: - 如果这个 extension 需要说明, 可以这样注释在这里extension MyViewcontroller: UITableViewDataSource {  // table view data source methods}// MARK: - UIScrollViewDelegateextension MyViewcontroller: UIScrollViewDelegate {  // scroll view delegate methods}复制代码

不推荐:

class MyViewcontroller: UIViewController, UITableViewDataSource, UIScrollViewDelegate {  // all methods}复制代码

无用代码

要将文件中无用的代码和注释删除掉,这样保持界面整洁

不推荐:

override func didReceiveMemoryWarning() {   super.didReceiveMemoryWarning()  // Dispose of any resources that can be recreated.}override func numberOfSectionsInTableView(tableView: UITableView) -> Int {   // #warning Incomplete implementation, return the number of sections   return 1}override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {  // #warning Incomplete implementation, return the number of rows  return Database.contacts.count}复制代码

推荐:

override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {  return Database.contacts.count}复制代码

空格

  • 使用空格而不使用 Tab,永远使用4个空格来表示换行, 而不要使用 Tab 来换行.
  • 你可以设置你的 IDE 自动将输入的 Tab 转换为空格.
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]}复制代码

每行的长度不应该超过120

每行的长度如果过长了话会影响阅读代码的体验, 比如有的开发者习惯左边和右边的 pannel 同时打开, 那么120个字符/行的长度是最适宜的, 这样可以保证在开关左右 pannel 的时候 IDE 不会出现换行, 你可以在 Xcode 中设置每行的最大长度的警告线 (Preferences > Text Editing > Page guide at column: 120)

不要使用 C 语言的括号形式 空格和换行

条件控制语句, 例如 if/else/while/switch 等的左括号应当在本行, 而右括号应当在新的一行.

推荐:

if user.isHappy {  // Do something} else {  // Do something else}复制代码

不推荐:

if user.isHappy{    // Do something}else {    // Do something else}复制代码

如果方法名过长需要换行了话, 在第二行空4格

当一个函数超过120的时候, 可以在适当的地方开始换行(如某个参数的结尾)

推荐:

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

注释

首先, 虽然注释很重要,但是最好的注释就是自文档化.注释是用来解释说明某段特定的代码做了些什么, 或者为什么这么做.同时,注释必须能否保持更新或删除.例外:这并不适用于那些用来生成文档的注释.

类的声明注释

每一个class、category或protocol都需要一个注释说明其作用及如何使用.   注释要包括概述、必要参数、版本信息、作者、更新信息, 如:

/// 说明该类的作用, 及如何使用////// 参数: 必要的参数////// @since 版本信息/// @author 更新作者 更新信息复制代码

另外, 如果是public的方法, 要对每个方法做注释, 包括参数、返回值以及注意情况等

实现中的注释

用竖线`` 代替引号“”, 这样能消除一些歧义, 如:

// Sometimes we need `count` to be less than zero. 或者本身带有引号的情况, 如:

// Remember to call `StringWithoutSpaces("foo bar baz")`

不要在注释中嵌入代码片段

不要在注释中嵌入代码片段, 因为代码要尽可能的自文档化, 并且不利于自动生成文档

对方法的描述以及引用

当涉及方法并包括所需的参数名称时, 应该从调用者的角度命名, 或使用_命名为缺省.

从你自己创建的init方法调用调用 convertPointAt(column:row:) .如果你调用dateFromString(_:)要确定你提供了一个格式为"yyyy-MM-dd"的字符串.如果你从 viewDidLoad()调用方法 timedAction(afterDelay:perform:) 记得提供一个调整延迟值和一个动作来执行此方法.你不应该直接调用数据源方法: tableView(_:cellForRowAtIndexPath:) .复制代码

如果有疑问, 可以参照 Xcode 在跳转栏的方法, 风格和它保持一致.

类和结构体

何时选用 Class, 何时选用 Struct?

在 Swift 中, Class 是引用类型, Struct 是值类型, 值类型有许多好处, 例如当你传递一个值类型给另一个对象是, 发生的是深拷贝(deep copy)操作, 不会出现改了后者就改了前者的问题, 还意味着线程安全, 虽然很有可能有人会说值类型传递是相对耗时的(的确, 相对引用类型, 传递值类型是会稍微耗时一点), 但是, 我们应当认为在如今的世界中, 这种不是复杂度提升级别的耗时(耗时为一个常量)相对于值类型能带来的好处是非常明显的, 所以我们建议在 Swift 中请优先考虑使用值类型. (关于性能问题如果你深陷网络"性能陷阱"不能自拔, 可以查阅 WWDC 2015 - Swift Value Type 来看看苹果官方的回答, 更可以遵循我组第二原则 : 没有真正看到性能问题时不要考虑性能问题), 下面我们就创建对象时的选用 Class 的情况做一个简单的描述:

1.当一个对象具有唯一性的时候选用 Class:  如果你要创建的对象是一个具有 identifer 的对象, 例如 User, Task, 哪怕两个 User 具有相同的名字, 但是不同的 UID 即表明他们不是同一个人, 那么这个时候 Class 更加合适, 但是比如这个 User.Birthday 可能是一个 Date(你自定义的 Date), 这个 Date 就更适合用 Struct, 因为两个用户如果都出生在10月1日, 那么这个10月1日是对等的.

2.不要和系统的 API 对着干:  如果系统的 API 要你传一个 Class, 那么就传一个 Class, 不要想方设法的对抗系统(尤其是使用只有你看得懂的黑魔法).

除非语法需要, 否则不要显式使用 self

在 Swift 中, 默认调用方法或者 property 的时候不需要使用 self.xxx 来调用, 另外, Swift 要求闭包(Closure)中使用 self.xxx 来显式的暗示 self 已经被 retain 了, 所以, 在正常的方法调用以及 property 的调用中, 不要使用 self.xxx 来调用以免产生歧义.

class BoardLocation {  let row: Int, column: Int  init(row: Int, column: Int) {    self.row = row    self.column = column        let closure = {      print(self.row)    }  }}复制代码

计算型属性 (Computed Property)

如果一个 Computed Property 是只读的, 则不要写 get:

推荐:

var diameter: Double {  return radius * 2}复制代码

不推荐:

var diameter: Double {  get {    return radius * 2  }}复制代码

Final

当自定义类不允许被继承时,应该用final标记这个类.例如:

// Box这个类不允许被继承final class Box
{  let value: T   init(_ value: T) {    self.value = value  }}复制代码

闭包(Closures)

尽可能使用尾随闭包语法. 在所有情况下, 闭包的参数需要有一个描述性的名称.

闭包中要注意循环引用情况,使用[weak self]解决此问题

推荐:

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}复制代码

类型 (Types)

尽量使用 Swift 的原生类型, Swift 可以通过桥接文件对 OC 进行调用. 对一个属性进行声明时, 倾向于同时声明属性的类型.

推荐:

let width = 120.0                                    // Doublelet widthString = (width as NSNumber).stringValue    // String复制代码

不推荐:

let width: NSNumber = 120.0                          // NSNumberlet widthString: NSString = width.stringValue        // NSString复制代码

在Sprite Kit代码中, 使用CGFloat, 如果它使代码更简洁, 避免过多的转换

常量 (Constants)

使用 let 定义常量,  var 定义变量. 如果一个变量不会发生变化, 尽量用 let 修饰而不是 var.   对于 Class 应当都使用 let 而不是 var, 因为 Class 是引用类型, var 和 let 的修饰符并不可以阻止其属性被修改

提示:一个好的技巧是用 let 定义一切属性, 只有当编译器提示时才改为 var.

类型推断 (Type Inference)

虽然通过编译器的类型推断使代码更加紧凑, 但是, 这意味着要求你的变量声明的可读性要比之前更高.   所以, 尽量给常量或变量声明带上确切的类型.

推荐:

let maximumWidth: CGFloat = 106.5复制代码

不推荐:

let maximumWidth = 106.5复制代码

可选类型 (Optionals)

  • 在nil值可能出现的情况下, 将变量跟函数返回值的类型通过?定义成Optional.
  • 只有在确定实例变量会在初始化之后才被使用的情况下, 通过 ! 将其定义为隐式解包类型(Implicitly Unwrapped Types),
  • 比如说会在viewDidLoad中被创建的子视图.  在访问一个Optional值时, 如果该值只被访问一次, 或者之后需要连续访问多个Optional值, 请使用链式Optional语法:
self.textContainer?.textLabel?.setNeedsDisplay()复制代码

if let 判断可选类型一次, 可以在作用域内做多个操作

if let textContainer = self.textContainer {  // do many things with textContainer}复制代码
  • 当命名可选变量或属性, 避免命名为比如:optionalString 或者 maybeView,因为他们的可选性已经在类型声明时明确了.
  • if let 判断语句尽量使解包前后变量名一致.

推荐:

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  }}复制代码
  • 不要使用强转符号(!)将一个 nullable 的对象转换为 nonnull 的对象, 绝大多数的崩溃错误都是由此引起的

  • 相对于if let 我们更偏好使用 guard let, 因为 guard 语法会强制你实现 else 的逻辑, 让错误的 handle 更加优雅

初始化结构体 (Struct Initializers)

尽量使用新的 Swift 的 API 而不是 Objective-C 的 bridge 后的 API:

推荐:

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)复制代码

推荐使用 Swift 形式的常量: Module.Constant, 而不是 C 形式的常量: ModuleConstant:

推荐:

CGRect.infinite, CGRect.null复制代码

不推荐:

CGRectInfinite, CGRectNull复制代码

懒加载 (Lazy Initialization)

使用延迟初始化的精细控制对象的生存期.对于延迟加载视图的视图控制器

style1.private lazy var locationManager: CLLocationManager = CLLocationManager()style2.private lazy var locationManager: CLLocationManager = { let locationManager = CLLocationManager() return locationManager}()style3.lazy var locationManager: CLLocationManager = self.makeLocationManager()private func makeLocationManager() -> CLLocationManager {  let manager = CLLocationManager()  manager.desiredAccuracy = kCLLocationAccuracyBest  manager.delegate = self  manager.requestAlwaysAuthorization()  return manager}复制代码

空数组和字典的类型批注 (Type Annotation for Empty Arrays and Dictionaries)

空数组和字典, 使用类型批注. 对指定的数组或字典, 多行文本, 请使用类型批注.

推荐:

var names: [String] = []var lookup: [String: Int] = [:]复制代码

不推荐:

var names = [String]()var lookup = [String: Int]()复制代码

语法糖 (Syntactic Sugar)

使用类型定义个快捷语法而不要使用完整的语法

推荐:

var deviceModels: [String]var employees: [Int: String]var faxNumber: Int?复制代码

不推荐:

var deviceModels: Array
var employees: Dictionary
var faxNumber: Optional
复制代码

内存管理 (Memory Management)

推荐:

resource.request().onComplete { [weak self] response in  guard let strongSelf = self else { return }  let model = strongSelf.updateModel(response)  strongSelf.updateUI(model)}复制代码

不推荐:

// 如果self在response返回之前就被释放了 可能会引发崩溃resource.request().onComplete { [unowned self] response in  let model = self.updateModel(response)  self.updateUI(model)}复制代码

不推荐:

resource.request().onComplete { [weak self] response in  let model = self?.updateModel(response)  self?.updateUI(model)}复制代码

流程控制 (Access Control)

控制流 (Control Flow)

遍历请使用for-in的格式, 而不是C语言风格的for-condition-increment的格式

推荐:

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() {  print(index)}复制代码

不推荐:

var i = 0while i < 3 {  print("Hello three times")  i += 1}var i = 0while i < attendeeList.count {  let person = attendeeList[i]  print("\(person) is at position #\(i)")  i += 1}复制代码

Golden Path

善用guard let 而不是嵌套if let 来做多个条件判断

推荐:

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 let来减少嵌套.例如:

推荐:

guard let number1 = number1, number2 = number2, 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")}复制代码

分号 (Semicolons)

  • Swift在任何语句后都不需要分号, 分号仅在你将多个语句写在一行时有用(用于区分语句)
  • 不要将多个语句写在一行然后用分号区分 这个规则的唯一例外是for语句, 它需要分号. 然而, 在可能的情况下尽量使用可选的 for-in 语句

推荐:

let swift = "not a scripting language"复制代码

不推荐:

let swift = "not a scripting language";复制代码

## 圆括号 (Parentheses)

不需要括号条件, 应予以删除.

推荐:

if name == "Hello" {  print("World")}复制代码

不推荐:

if (name == "Hello") {  print("World")}复制代码

总结

无规矩不成方圆,愿这篇文章能够帮助到大家。本人会不定时更新,如有异议,请issue我。

转载于:https://juejin.im/post/5aa542bdf265da23826d9b26

你可能感兴趣的文章
python 冒泡排序,快排
查看>>
二分查找
查看>>
中文分词JCseg的MMSEG算法。
查看>>
[XD5.5]如何关闭XD的Audio UDP通道
查看>>
css3学习 理论之其他
查看>>
数学图
查看>>
【Android-View】点击侧滑菜单(SlidingMenu)按钮,更新主题内容时容易引发的内存问题解决方案...
查看>>
【Android-视频播放】实用vitamio自定义控制条位置
查看>>
返回函数多个返回值
查看>>
启用PowerShell Web Access
查看>>
django文件上传下载
查看>>
Maven 运行启动时****找不到符号*com.xxx.user.java
查看>>
Android客户端性能测试常见指标及测试方法--转载
查看>>
批量ping 检测linux主机是否可以通
查看>>
【转】搭建高可用mongodb集群(一)——配置mongodb
查看>>
DEDE织梦自定表单提交后自动发送邮件并到站长邮箱
查看>>
widget(7、dialog)
查看>>
jvm08
查看>>
Class.forName("ClassName")与ClassName.class的区别
查看>>
java中把对象、对象bean、list集合、对象数组、Map和Set以及字符串转换成Json
查看>>