MK
摩柯社区 - 一个极简的技术知识社区
AI 面试

Swift代码风格与规范

2024-10-235.5k 阅读

命名规范

在Swift编程中,清晰且一致的命名对于代码的可读性和可维护性至关重要。

变量和常量命名

  1. 描述性命名:变量和常量的命名应该能够准确描述其用途。例如,如果你在计算用户的年龄,命名为userAge就比简单的age更具描述性,如果在一个电商应用中,表示商品价格,productPrice就比p要好得多。
let userAge = 25
let productPrice: Double = 19.99
  1. 遵循驼峰命名法:Swift中使用骆驼命名法,即首字母小写,后续单词首字母大写。像firstNamelastLoginDate这样的命名是符合规范的。避免使用下划线来分隔单词,虽然在某些情况下Swift允许使用下划线,但这不是推荐的风格。
var lastName = "Smith"
var userLoginCount = 5
  1. 避免使用缩写:除非缩写是非常普遍且易于理解的,如URLHTTP等。尽量使用完整的单词,这样代码更易于阅读和理解。例如,使用numberOfItems而不是numOfItems
let numberOfStudents = 30
  1. 常量命名:常量通常使用和变量相同的命名规则,但为了突出其不可变性,有些开发者喜欢将常量名全部大写并用下划线分隔单词,不过这不是Swift官方推荐的方式。官方推荐还是使用驼峰命名法。例如,定义一个表示圆周率的常量,推荐使用let pi = 3.14159,而不是LET_PI = 3.14159
let maximumValue = 100

函数命名

  1. 以动词开头:函数的命名应该以一个动词开头,描述函数的行为。例如,calculateTotalfetchDatavalidateInput等。这样命名可以让调用者一眼就能明白函数的作用。
func calculateTotal(prices: [Double]) -> Double {
    var total = 0.0
    for price in prices {
        total += price
    }
    return total
}
  1. 参数标签:为函数的参数提供有意义的标签。参数标签在调用函数时会显示,有助于清晰地表明每个参数的用途。例如,func greet(person: String, withMessage message: String),在调用时greet(person: "John", withMessage: "Hello!"),这样的参数标签使得代码的意图非常明确。
func sendEmail(to recipient: String, withSubject subject: String, body: String) {
    // 发送邮件的逻辑
    print("Sending email to \(recipient) with subject \(subject) and body \(body)")
}
sendEmail(to: "example@mail.com", withSubject: "Swift Coding Style", body: "This is about Swift code style...")
  1. 简洁明了:虽然函数名要描述性强,但也不要过于冗长。尽量保持简洁,准确传达函数的核心功能。例如,filterValidUsers就比filterUsersThatAreConsideredValidAccordingToCertainCriteria要简洁且表意明确。
func filterValidUsers(users: [User]) -> [User] {
    return users.filter { $0.isValid }
}

类型命名

  1. 类、结构体和枚举命名:类、结构体和枚举的命名应该采用驼峰命名法,首字母大写,并且名字应该是名词,描述类型所代表的事物。例如,User类、Rectangle结构体、Color枚举。
class User {
    var name: String
    var age: Int
    init(name: String, age: Int) {
        self.name = name
        self.age = age
    }
}
struct Rectangle {
    var width: Double
    var height: Double
    func area() -> Double {
        return width * height
    }
}
enum Color {
    case red
    case green
    case blue
}
  1. 协议命名:协议命名同样采用驼峰命名法,首字母大写,通常以ableibleer结尾,以表明遵循该协议的类型具备某种能力或扮演某种角色。例如,Comparable协议表示类型可以进行比较,DataSource协议表示提供数据的来源。
protocol Payable {
    func calculatePayment() -> Double
}
class Employee: Payable {
    var salary: Double
    init(salary: Double) {
        self.salary = salary
    }
    func calculatePayment() -> Double {
        return salary
    }
}

代码布局

良好的代码布局能够让代码结构清晰,便于阅读和理解。

缩进

  1. 使用4个空格:在Swift中,推荐使用4个空格进行缩进。制表符(Tab)在不同编辑器中的显示宽度可能不同,会导致代码在不同环境下布局不一致,而空格则能保证一致性。
if someCondition {
    let message = "Condition is true"
    print(message)
} else {
    let message = "Condition is false"
    print(message)
}
  1. 嵌套结构的缩进:对于嵌套的代码块,如循环内的条件判断,要根据嵌套层次进行相应的缩进。这样可以清晰地显示代码的逻辑结构。
for i in 0..<10 {
    if i % 2 == 0 {
        print("\(i) is even")
    } else {
        print("\(i) is odd")
    }
}

空行

  1. 分隔逻辑块:使用空行来分隔不同的逻辑块,使代码结构更清晰。例如,在类中,不同的属性定义和方法定义之间可以使用空行分隔;在函数中,不同的功能段落之间也可以使用空行分隔。
class MyClass {
    var property1: Int
    var property2: String
    
    init(property1: Int, property2: String) {
        self.property1 = property1
        self.property2 = property2
    }
    
    func method1() {
        // 方法1的逻辑
    }
    
    func method2() {
        // 方法2的逻辑
    }
}
func complexFunction() {
    // 初始化部分
    let initialValue = 10
    
    // 计算部分
    let result = initialValue * 2
    
    // 输出部分
    print("The result is \(result)")
}
  1. 避免过多空行:虽然空行有助于分隔代码,但过多的空行可能会使代码显得松散,降低可读性。一般来说,每个逻辑块之间使用1 - 2个空行即可。

代码文件组织

  1. 单一职责原则:每个Swift文件应该专注于一个主要的功能或类型。例如,一个文件定义一个类,一个文件定义一组相关的函数,或者一个文件定义一个枚举。这样可以使代码更易于管理和维护。
  2. 文件命名:文件命名应该与其中定义的主要类型或功能相关,采用驼峰命名法,首字母大写。例如,如果文件定义了User类,文件可以命名为User.swift;如果是一组处理数学计算的函数,文件可以命名为MathFunctions.swift
  3. 导入语句:导入语句应该放在文件的开头,在任何类型或函数定义之前。如果有多个导入语句,按照字母顺序排列,这样可以使导入部分更整齐。
import Foundation
import UIKit

代码格式

正确的代码格式不仅能让代码看起来美观,还能增强其可读性。

运算符

  1. 空格使用:在运算符两侧添加空格,使表达式更易读。例如,let result = a + blet result=a+b更清晰。对于复合运算符,如+=*=等,同样在两侧添加空格。
let sum = 5 + 3
let product = 4 * 2
let total = 0
total += 10
  1. 运算符优先级与括号:根据运算符的优先级,合理使用括号来明确运算顺序。即使优先级是明确的,适当使用括号也能增强代码的可读性。例如,let value = (a + b) * clet value = a + b * c更清晰,尤其是在复杂表达式中。
let x = 2
let y = 3
let z = 4
let result = (x + y) * z

语句

  1. 一行一条语句:一般情况下,一条语句占一行,这样可以使每行代码的逻辑更清晰,便于调试和阅读。例如,不要写成let a = 1; let b = 2,而应该写成:
let a = 1
let b = 2
  1. 长语句换行:如果一条语句过长,需要换行时,在运算符处换行,并在下一行缩进4个空格。例如:
let longText = "This is a very long text that needs to be wrapped " +
    "onto the next line to keep the code readable."
let result = veryLongFunctionCall(withParameter1: parameter1,
    withParameter2: parameter2,
    withParameter3: parameter3)

注释

  1. 单行注释:用于对一行代码或局部代码进行简短解释。单行注释以//开头。例如:
// 计算两个数的和
let sum = a + b
  1. 多行注释:用于对一段代码块或复杂功能进行详细说明。多行注释以/*开头,*/结尾。例如:
/*
这段代码实现了用户登录的功能。
首先检查用户名和密码是否为空,
然后验证用户名和密码是否匹配。
如果匹配则登录成功,否则返回错误信息。
*/
func login(username: String, password: String) -> Bool {
    if username.isEmpty || password.isEmpty {
        return false
    }
    // 验证逻辑
    return true
}
  1. 文档注释:使用///开头的注释用于生成文档。这种注释可以包含对函数、类、结构体等的详细描述,参数说明,返回值说明等。Xcode会根据这些注释生成代码文档。例如:
/// 计算两个整数的乘积
/// - Parameter a: 第一个整数
/// - Parameter b: 第二个整数
/// - Returns: 两个整数的乘积
func multiply(a: Int, b: Int) -> Int {
    return a * b
}

控制流语句

控制流语句在Swift中用于控制程序的执行流程,正确使用它们对于代码的逻辑正确性和可读性很重要。

if语句

  1. 基本格式if语句的基本格式为if condition { statements },条件表达式必须用括号括起来,语句块用花括号括起来。即使语句块只有一行代码,也建议使用花括号,以增强代码的可读性和可维护性。
let number = 10
if number > 5 {
    print("\(number) is greater than 5")
}
  1. if - else语句if - else用于在条件为真和为假时执行不同的代码块。
let age = 18
if age >= 18 {
    print("You are an adult")
} else {
    print("You are a minor")
}
  1. if - else if - else语句:用于多个条件的判断,按照条件的顺序依次检查,一旦某个条件为真,就执行对应的代码块,后续条件不再检查。
let score = 85
if score >= 90 {
    print("A")
} else if score >= 80 {
    print("B")
} else if score >= 70 {
    print("C")
} else {
    print("D")
}

switch语句

  1. 简单匹配switch语句用于对一个值进行多种情况的匹配。switch的表达式不需要用括号括起来,每个case分支用:分隔,并且不需要使用break语句,除非你想提前结束switch语句。
let day = 3
switch day {
case 1:
    print("Monday")
case 2:
    print("Tuesday")
case 3:
    print("Wednesday")
default:
    print("Other day")
}
  1. 区间匹配switch可以使用区间匹配。例如,匹配一个整数在某个区间内。
let number = 15
switch number {
case 1...10:
    print("Number is between 1 and 10")
case 11...20:
    print("Number is between 11 and 20")
default:
    print("Number is outside the range")
}
  1. 元组匹配switch还可以对元组进行匹配,同时匹配多个值。
let point = (2, 3)
switch point {
case (0, 0):
    print("Origin")
case (_, 0):
    print("On the x - axis")
case (0, _):
    print("On the y - axis")
default:
    print("Other point")
}

for循环

  1. 遍历数组for - in循环用于遍历数组、集合等序列。
let numbers = [1, 2, 3, 4, 5]
for number in numbers {
    print(number)
}
  1. 遍历范围:可以使用for - in遍历一个范围,如0..<5表示从0到4。
for i in 0..<5 {
    print("Index \(i)")
}
  1. 带间隔的遍历:可以使用stride(from:to:by:)函数实现带间隔的遍历。
for i in stride(from: 0, to: 10, by: 2) {
    print(i)
}

while循环

  1. while循环while循环在条件为真时重复执行代码块。
var count = 0
while count < 5 {
    print("Count is \(count)")
    count += 1
}
  1. do - while循环:Swift中没有传统的do - while循环,但可以通过repeat - while实现类似功能,它会先执行一次代码块,然后再检查条件。
var number = 10
repeat {
    print("Number is \(number)")
    number -= 1
} while number > 0

函数和闭包

函数和闭包是Swift中重要的代码组织和功能实现方式,遵循良好的风格规范有助于代码的复用和维护。

函数定义

  1. 参数和返回值:在定义函数时,明确指定参数的类型和返回值类型。如果函数没有返回值,返回类型为Void或省略不写。
func addNumbers(a: Int, b: Int) -> Int {
    return a + b
}
func printMessage() {
    print("This is a message")
}
  1. 默认参数值:可以为函数参数提供默认值,这样在调用函数时如果没有传入该参数,就会使用默认值。
func greet(name: String, message: String = "Hello") {
    print("\(message), \(name)")
}
greet(name: "John") // 输出: Hello, John
greet(name: "Jane", message: "Hi") // 输出: Hi, Jane

闭包

  1. 闭包表达式:闭包表达式是一种简洁的定义闭包的方式。例如,对一个数组进行排序,可以使用闭包来定义排序规则。
let numbers = [5, 2, 8, 1, 9]
let sortedNumbers = numbers.sorted { $0 < $1 }
print(sortedNumbers)
  1. 尾随闭包:当闭包是函数的最后一个参数时,可以使用尾随闭包的语法,将闭包写在函数括号外面,使代码更简洁。
DispatchQueue.main.async {
    print("This code runs on the main queue")
}
  1. 捕获值:闭包可以捕获其定义上下文中的常量和变量,即使这些常量和变量在闭包执行时已经超出了原来的作用域,它们的值依然会被闭包保存。
func makeIncrementer(forIncrement amount: Int) -> () -> Int {
    var runningTotal = 0
    func incrementer() -> Int {
        runningTotal += amount
        return runningTotal
    }
    return incrementer
}
let incrementByTen = makeIncrementer(forIncrement: 10)
print(incrementByTen()) // 输出: 10
print(incrementByTen()) // 输出: 20

面向对象编程(类和结构体)

Swift中的类和结构体是面向对象编程的基础,遵循规范的使用方式可以使代码结构更清晰。

  1. 属性和方法:类的属性定义应该尽量靠近类的顶部,按照一定的逻辑顺序排列,例如先定义存储属性,再定义计算属性。方法定义在属性之后,同样按照功能逻辑分组。
class Person {
    var name: String
    var age: Int
    
    init(name: String, age: Int) {
        self.name = name
        self.age = age
    }
    
    func introduce() {
        print("Hi, I'm \(name) and I'm \(age) years old")
    }
}
  1. 继承:在定义继承类时,将父类名放在类名之后,用冒号分隔。重写父类方法时,使用override关键字。
class Student: Person {
    var grade: Int
    
    init(name: String, age: Int, grade: Int) {
        self.grade = grade
        super.init(name: name, age: age)
    }
    
    override func introduce() {
        print("Hi, I'm \(name), \(age) years old and in grade \(grade)")
    }
}

结构体

  1. 值类型特点:结构体是值类型,当被赋值或传递时,会进行值拷贝。结构体的定义和类类似,但结构体不能继承。
struct Point {
    var x: Double
    var y: Double
    
    func distance(to other: Point) -> Double {
        let dx = x - other.x
        let dy = y - other.y
        return sqrt(dx * dx + dy * dy)
    }
}
let point1 = Point(x: 0, y: 0)
let point2 = Point(x: 3, y: 4)
let distance = point1.distance(to: point2)
  1. 初始化:结构体可以有自定义的初始化器,也会自动生成成员逐一初始化器,方便初始化结构体实例。
struct Size {
    var width: Double
    var height: Double
}
let size = Size(width: 100, height: 200)

协议和扩展

协议和扩展为Swift提供了强大的功能扩展能力,遵循良好的规范可以使代码更灵活和可维护。

协议

  1. 协议定义:协议定义了一组方法、属性等要求,遵循协议的类型必须实现这些要求。协议名采用驼峰命名法,首字母大写。
protocol Drawable {
    func draw()
}
  1. 协议继承:协议可以继承其他协议,增加更多的要求。
protocol Fillable: Drawable {
    func fill(color: String)
}
  1. 协议遵循:类、结构体和枚举可以遵循协议,通过:后跟协议名来声明。
class Rectangle: Drawable {
    func draw() {
        print("Drawing a rectangle")
    }
}

扩展

  1. 扩展现有类型:扩展可以为现有的类、结构体、枚举等类型添加新的功能,而无需修改原始类型的定义。扩展使用extension关键字。
extension Int {
    func squared() -> Int {
        return self * self
    }
}
let number = 5
let squaredNumber = number.squared()
  1. 扩展协议:可以对协议进行扩展,为遵循该协议的类型提供默认实现。
extension Drawable {
    func drawDefault() {
        print("Default drawing implementation")
    }
}
class Circle: Drawable {
    func draw() {
        drawDefault()
        print("Drawing a circle")
    }
}

通过遵循以上这些Swift代码风格与规范,能够编写出结构清晰、易于阅读和维护的高质量代码,无论是在个人项目还是团队协作项目中,都能提高开发效率和代码的可扩展性。在实际开发过程中,团队可以根据自身情况制定更详细的内部规范,但基础的这些原则是通用且重要的。