Swift开发篇 --- 编码规范

1.命名

1.1 类型

  • 类型名称(如 struct, enum, class, typedef, associatedtype, protocol 等)使用大驼峰命名法命名
  • 变量和常量则以小驼峰命名法命名
enum Planet {
  case mercury, venus, earth, mars, jupiter, saturn, uranus,neptune
}

struct Person {
  var mobileNo: String
}

1.2 Protocol

根据苹果接口设计指导准则,协议名称用来描述一些东西是什么的时候是名词,例如:Collection, WidgetFactory。若协议名称用来描述能力应该以 -ing, -able, 或 -ible 结尾,例如:Equatable, Resizing。

1.3 类前缀

Swift使用模块名作为 namespace,在使用了Podfile的工程中,主工程、PodA、PodB…PodN 各自在自己的Module中,不会发生模块间同名文件/同名变量的命名冲突。因此在Swift中可以不使用前缀,这点跟OC有区别。

1.4 方法

如果返回Bool值,则命名上应该体现出判断的意思,尽量带上should/would/will/is/can等词语

func shouldCancel(request: Request) -> Bool {

}

1.5 委托

  • 在自定义Delegate的方法命名时使用代理源作为第一个未命名参数。未命名参数指使用_标记的参数。为什么要默认添加代理源?为了方便协议实现方在Delegate方法中判断本次回调来源于哪个source

推荐:

func namePickerView(_ namePickerView: NamePickerView, didSelectName name: String)
func namePickerViewShouldReload(_ namePickerView: NamePickerView) -> Bool

不推荐:

func didSelectName(namePicker: NamePickerViewController, name: String)
func namePickerShouldReload() -> Bool
  • 当使用委托和协议时,请注意避免出现循环引用,基本上是在定义属性的时候使用 weak 修饰。
  • 当一个对象要实现协议一致性时,推荐使用 extension 隔离协议中的方法集,这样让相关方法和协议集中显示在一起,也简化了类支持一个协议和实现相关方法的流程

推荐:

class MyViewcontroller: UIViewController {
      // 方法
} 

extension MyViewcontroller: UITableViewDataSource {
      // UITableViewDataSource 方法
}

extension MyViewcontroller: UIScrollViewDelegate {
      // UIScrollViewDelegate 方法
} 

不推荐:

class MyViewcontroller: UIViewController, UITableViewDataSource, UIScrollViewDelegate {
       // 所有的方法
}

1.6 泛型

泛型变量命名应该语义清晰并且使用首字母大写驼峰命名。如果实在无法明确语义,则优先使用T、U、V这几个大家普遍约定的缩写,方便代码阅读者推断其为泛型类型

推荐:

struct Stack<Element>{ ... }
func writeTo <Target: OutputStream>(to Target: inout Target)
func swap(_ 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)

2.代码逻辑

2.1 self推断

  • 让编译器在所有允许的地方推断 self
  • 在 init 中设置参数时显性地使用 self 。
  • 在 non-escaping closures 中显性地使用 self 。
  • 在避免命名冲突时使用 self 。

例:

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

2.2 常量

  • 类常量应该在类型里声明为 static。使用 static 修饰常量可以允许他们在被引用的时候不需要实例化类型。
  • 除了单例以外,应尽量避免生成全局常量。
  • 定义常量使用 let 关键字,定义变量使用 var 关键字, 如果变量的值未来不会发生变化要使用常量。

例:

struct PhysicsModel {
    static var speedOfLightInAVacuum = 299_792_458
}

class Spaceship {
    static let topSpeed = PhysicsModel.speedOfLightInAVacuum
    var speed: Double

    func fullSpeedAhead() {
        speed = Spaceship.topSpeed
    }
}

2.3 计算型类型属性

  • 当只需要继承 getter 方法时,返回简单的 Computed 属性即可。

正确:

class Example {
    var age: UInt32 {
        return arc4random()
    }
}

错误:

class Example {
    var age: UInt32 {
        get {
            return arc4random()
        }
    }
}
  • 如果你在属性中添加了 set 或者 didSet ,那么你应该显示地提供 get 方法。
class Person {
    var age: Int {
        get {
            return Int(arc4random())
        }
        set {
            print("That's not your age.")
        }
    }
}

2.4 单例

Swift 中单例很简单,Swift 的 runtime 会保证单例的创建并且采用线程安全的方式访问:

class ControversyManager {
    static let sharedInstance = ControversyManager()
    private init() {}
}

单例通常只需要访问 sharedInstance 的静态属性,除非你有不得已的原因去重命名它。注意,不要用静态函数或者全局函数去访问你的单例。

2.5 错误处理

可以使用 do/try/catch 机制,避免使用 try! 和 try?

2.6 可选值

  • 声明一个函数的某个参数可以为 nil 时,用?
  • 当你确定某个变量在使用时已经确定不是 nil 时,在后面加 !

2.7 数据类型

优先使用Swift 数据类型,实在实现不了才使用Objective-C的数据类型

推荐:

let width = 120.0                   // Double
let widthString = "\(width)"        // String

不推荐,因为需要类型转化:

let width = 120.0                                    // Double
let widthString = (width as NSNumber).stringValue    // String

2.8 类型推断

变量声明时更推荐让编译器做类型推断;但空集合类型需要显式写明类型

推荐:

let message = "Click the button"
let currentBounds = computeViewBounds()
var names = ["Mic", "Sam", "Christine"]
let maximumWidth: CGFloat = 106.5
var names: [String] = []
var lookup: [String: Int] = [:]

不推荐:

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

2.9 流程控制

2.9.1 优先使用for-in、forEach的swift风格循环控制,而不是while的C风格

推荐:

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
}

2.9.2 三元操作符?:

建议只用于做简单的赋值操作,不要三元组嵌套三元组,否则会降低代码可读性;有复杂判断时应使用if判断拆分逻辑

推荐:

let value = 5
result = (value != 0 ? x : y)

let isHorizontal = true
result = (isHorizontal ? x : y)

不推荐:

result = a > b ? x = c > d ? c : d : y

2.9.3 if等嵌套层级

  • 必要时要使用guard代替if降低嵌套层级

推荐:

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, number2=number2, number3=number3 else{
    fatalError("impossible") 
}
// 处理number

2.9.4 Switch 模块中不用显式使用break

3. 代码结构

3.1 左大括号不用另起一行:

class SomeClass {
    func someMethod() {
        if x == y {
            /* ... */
        } else if x == z {
            /* ... */
        } else {
            /* ... */
        }
    }
    /* ... */
}

3.2 空格

3.2.1 逗号后面加空格

let myArray = [1, 2, 3, 4, 5]

3.2.2 运算符的前后添加空格

let myValue = 20 + (30 / 2) * 3
if 1 + 1 == 3 {
    fatalError("The universe is broken.")
}
func pancake() -> Pancake {
    /* ... */
}

3.2.3 类型声明时:紧跟属性,与类型之间加空格

推荐:

let width = 120.0 
let height: Double = 120.0 
var title: String

不推荐:

let width=1.0
let height:Double  =  1.0
var title:String

3.2.4 方法与方法之间或类与类、枚举、结构体之间空一行,不可留置不必要的多空行

3.4 当调用的函数有多个参数时,每一个参数另起一行,并比函数名多一个缩进。

someFunctionWithManyArguments(
    firstArgument: "Hello, I am a string",
    secondArgument: resultFromSomeFunction()
    thirdArgument: someOtherLocalVariable)

3.5 当遇到需要处理的数组或字典内容较多需要多行显示时,需把 [ 和 ] 类似于方法体里的括号,方法体里的闭包也要做类似处理

someFunctionWithABunchOfArguments(
    someStringArgument: "hello I am a string",
    someArrayArgument: [
        "dadada daaaa daaaa dadada daaaa daaaa dadada daaaa daaaa",
        "string one is crazy - what is it thinking?"
    ],
    someDictionaryArgument: [
        "dictionary key 1": "some value 1, but also some more text here",
        "dictionary key 2": "some value 2"
    ],
    someClosure: { parameter1 in
        print(parameter1)
    })

3.6 在写枚举类型的时候,尽量简写。

// 推荐
imageView.setImageWithURL(url, type: .person)
// 不推荐
imageView.setImageWithURL(url, type: AsyncImageView.Type.person)

3.7 默认的访问控制修饰符是 internal, 如果需要使用internal 可以省略不写

3.8 注释

  • 使用XCode自带的注释功能
  • 使用 //MARK: 做代码分隔

3.9 缩进:遵守 Xcode 内置的缩进格式

本文作者:Rui
发布时间:2021-05-12, 21:54:54
原始链接:https://wrf8.github.io/posts/2770607365.html
版权声明:转载请保留原文链接及作者。

×

喜欢就点赞,疼爱就打赏