Xcode — Swift

官方文档 #

官方文档:The Swift Programming Language

https://developer.apple.com/documentation/swift

语言特点 #

https://www.swift.org/about/

Swift is a general-purpose programming language that’s approachable for newcomers and powerful for experts. It is fast, modern, safe, and a joy to write.

变量声明 #

swift 使用 let 来声明常量,使用 var 来声明变量。常量只能赋值一次。即:let 的内存地址不能变,var 的内存地址可变。

// 只能赋值一次,再次赋值会报错
let hello = "world"
print(hello)

// hello 可以多次赋值
var hello = "world"
print(hello)
print("hello \(hello)")

声明时可以不指定类型,编译器会自动推断其类型,当然也可以像下面这样指定:

let label: String = "The width is"

数据类型 #

Int #

Int 是有符号整型,默认是当前平台的字长(如 32 位或 64 位),Uint 是无符号整型。

let age: Int = 25
let smallNumber: Int8 = 127
let largeNumber: Int64 = 9223372036854775807

Float 和 Double #

Double 是默认使用的浮点类型,64 位精度,Float 是 32 位。

let pi: Float = 3.14159
let pi: Double = 3.141592653589793

在 Swift 语言中,你可以使用下划线来提高长数字的可读性。比如:

let latitude: Double = 34.011_286
let longitude: Double = -118.495_308

print("Latitude: \(latitude)")  // 输出: Latitude: 34.011286
print("Longitude: \(longitude)") // 输出: Longitude: -118.495308

另外,在 JavaScript 中,同样可以使用下划线来提高可读性:

const latitude = 34.011_286;
const longitude = -118.495_308;

console.log(`Latitude: ${latitude}`);  // 输出: Latitude: 34.011286
console.log(`Longitude: ${longitude}`); // 输出: Longitude: -118.495308

Bool #

Bool 类型的可选值只有 truefalse

let isSwiftAwesome: Bool = true

String #

双引号在 Swift 中用于定义字符串(String)类型。字符串可以是多个字符的组合,也可以包含转义字符和字符串插值。

// 定义字符串
let greeting: String = "Hello, World!"

// 包含转义字符的字符串
let quote: String = "He said, \"Swift is awesome!\""

// 字符串插值
let name: String = "Alice"
let message: String = "Hello, \(name)"

Character #

特别需要注意的是,Swift 实际上不支持单引号来定义字符。单引号使用在 Swift 语言中并没有像某些其他编程语言(如 C 或 Java)那样的用途。在 Swift 中,字符也是用双引号表示的,只是字符类型是 Character

// 正确的字符定义(注意仍然使用双引号)
let letter: Character = "A"
let emoji: Character = "😊"
// 错误示例:let invalidCharacter: Character = "AB"  // 会报编译错误

恒等运算符 #

详细了解请参考:

在 Swift 中,恒等运算符(Identity Operators)用于判断两个对象实例是否引用同一个内存地址。为了进行这种比较,Swift 提供了两个恒等运算符:

  • ===(恒等): 判断两个引用类型(如类实例)是否引用同一个对象。
  • !==(不恒等): 判断两个引用类型是否引用不同的对象。

=== 运算符用于判断两个引用类型实例是否完全相同,即它们是否引用了同一个内存地址。

class Person {
    var name: String
    init(name: String) {
        self.name = name
    }
}

let person1 = Person(name: "Alice")
let person2 = person1
let person3 = Person(name: "Alice")

// person1 和 person2 指向同一个实例
if person1 === person2 {
    print("person1 and person2 refer to the same instance.")
} else {
    print("person1 and person2 do not refer to the same instance.")
}

// person1 和 person3 指向不同的实例,但其内容相同
if person1 === person3 {
    print("person1 and person3 refer to the same instance.")
} else {
    print("person1 and person3 do not refer to the same instance.")  // 这一句会执行
}

!== 运算符用于判断两个引用类型实例是否不同,即它们是否引用了不同的内存地址。

if person1 !== person3 {
    print("person1 and person3 do not refer to the same instance.")
} else {
    print("person1 and person3 refer to the same instance.")
}

注意事项

  1. 引用类型: 恒等运算符仅适用于引用类型(如类实例),因为它们比较的是内存地址。对于值类型(如结构体、枚举和基本数据类型),你应该使用等号 == 来比较其内容。

  2. 内容比较: 如果你需要判断两个实例的内容是否相同(而不是它们是否引用同一个对象),你应当实现相应的 Equatable 协议并使用 == 运算符。

值类型比较示例

struct Point: Equatable {
    var x: Int
    var y: Int
}

let point1 = Point(x: 1, y: 1)
let point2 = Point(x: 1, y: 1)

if point1 == point2 {
    print("point1 and point2 have the same content.")
} else {
    print("point1 and point2 have different content.")
}

实现 Equatable 协议 #

如果你创建了一个自定义类或结构体,并希望比较其内容相等性,你可以通过实现 Equatable 协议来实现内容比较。

class Person: Equatable {
    var name: String

    init(name: String) {
        self.name = name
    }

    // 实现 Equatable 协议的方法
    static func == (lhs: Person, rhs: Person) -> Bool {
        return lhs.name == rhs.name
    }
}

let person1 = Person(name: "Alice")
let person2 = Person(name: "Alice")

if person1 == person2 {
    print("person1 and person2 have the same content.")
} else {
    print("person1 and person2 have different content.")
}

总之,使用恒等运算符(===!==)可以帮助你确保你在处理的是同一个对象实例,而在比较值类型或对象内容时,应使用 ==!= 运算符。了解这两者的区别和使用场景可以帮助你编写更安全且高效的代码。

可选类型 ?! #

Swift 中可选类型的使用

类型转换 #

值永远不会被隐式转换为其他类型。如果你需要把一个值转换成其他类型,请显式转换。

let label = "The width is"
let width = 94
let widthLabel = label + String(width)

也可以在通过反斜杠\()在字符串中打印出来。

let apples = 3
let oranges = 5
let appleSummary = "I have \(apples) apples."
let fruitSummary = "I have \(apples + oranges) pieces of fruit."

数据结构 #

数组 Array #

有序的集合,可以存储相同类型的元素。

let emptyArray: [String] = []
let numbers: [Int] = [1, 2, 3, 4, 5]
let names: [String] = ["Alice", "Bob", "Charlie"]

集合 Set #

无序且唯一的集合,可以存储相同类型的元素。

let uniqueNumbers: Set<Int> = [1, 2, 3, 4, 4, 5]
// uniqueNumbers = [1, 2, 3, 4, 5]

字典 Dictionary #

无序的键值对集合,其中键是唯一的。

let studentGrades: [String: String] = ["Alice": "A", "Bob": "B"]
let emptyDictionary: [String: Float] = [:]

元组 Tuple #

用于将多个值组合成一个复合值,不要求类型相同。

let http404Error = (404, "Not Found")
let person = (name: "Alice", age: 25)
print(person.name)  // 输出: Alice
print(person.age)   // 输出: 25

结构体 Struct #

struct ReplaxModel: Hashable, Codable {
    var id: Int
    var name: String
    var park: String
    var state: String
    var description: String
    
    private var imageName: String
}

下标操作(subscript) #

在 Swift 中,下标(Subscript)是用于访问集合、列表或序列中特定位置的元素的一种语法。下标允许你使用类似数组的语法来读取和写入集合内的值。下标可以用于各种数据结构,如数组(Array)、字典(Dictionary)、字符串(String)等。除此之外,Swift 还允许自定义类型定义自己的下标操作。

数组中的下标 #

对于数组,Swift 提供了基于整数索引的下标操作,这和其他编程语言类似。

var fruits = ["Apple", "Banana", "Cherry"]
let firstFruit = fruits[0]  // 访问第一个元素,返回 "Apple"
fruits[1] = "Blueberry"     // 修改第二个元素的值为 "Blueberry"
print(fruits)               // 输出 ["Apple", "Blueberry", "Cherry"]

字典中的下标 #

字典的下标操作允许使用键来访问或修改值。

var ages = ["Alice": 25, "Bob": 28]
let aliceAge = ages["Alice"]  // 访问 Alice 的年龄,返回 25
ages["Bob"] = 30               // 修改 Bob 的年龄为 30
print(ages)                    // 输出 ["Alice": 25, "Bob": 30]

自定义下标 #

Swift 允许你为自己的自定义类型定义下标。通过实现下标,可以让你自己定义的数据结构也支持下标操作。

struct Matrix {
    let rows: Int, columns: Int
    var grid: [Double]
    
    init(rows: Int, columns: Int) {
        self.rows = rows
        self.columns = columns
        self.grid = Array(repeating: 0.0, count: rows * columns)
    }
    
    func indexIsValid(row: Int, column: Int) -> Bool {
        return row >= 0 && row < rows && column >= 0 && column < columns
    }
    
    subscript(row: Int, column: Int) -> Double {
        get {
            assert(indexIsValid(row: row, column: column), "Index out of range")
            return grid[(row * columns) + column]
        }
        set {
            assert(indexIsValid(row: row, column: column), "Index out of range")
            grid[(row * columns) + column] = newValue
        }
    }
}

var matrix = Matrix(rows: 2, columns: 2)
matrix[0, 1] = 1.5
matrix[1, 0] = 3.2
print(matrix[0, 1])  // 输出 1.5
print(matrix[1, 0])  // 输出 3.2

在这个示例中,Matrix 结构体定义了一个二维数组(矩阵)的下标,使得你可以通过 matrix[row, column] 这种方式来访问和修改矩阵中的元素。

下标的定义 #

自定义下标时,可以在结构体、类、枚举中定义一个或多个下标。下标可以接受一个或多个输入参数,并可以返回一个值。下标语法如下:

subscript(parameters) -> ReturnType {
    get {
        // 返回适当的值
    }
    set(newValue) {
        // 执行适当的赋值操作
    }
}
  • parameters:输入参数的名称和类型。
  • ReturnType:下标操作返回值的类型。
  • get 和 set 代码块:定义了如何获取和设置值。

下标定义支持计算属性的所有功能,例如可以读取和写入,也可以定义只读下标。

总结 #

  • 在 Swift 中,下标(Subscript)提供了一种简洁的语法,用于访问集合类型(如数组和字典)中的元素。
  • 下标可以重载,允许自定义类型提供自己的下标操作。
  • 下标可以是读取和写入的,也可以是只读的。

通过下标,可以使代码更直观和简洁,特别是在处理集合类型的数据结构时。

程序控制 #

if 判断语句 #

let username = "admin"
let password = "1234"

if username == "admin" && password == "1234" {
    print("Login successful")
} else {
    print("Login failed")
}

if-let 语句 #

You can use if and let together to work with values that might be missing. These values are represented as optionals. An optional value either contains a value or contains nil to indicate that a value is missing. Write a question mark (?) after the type of a value to mark the value as optional.

if let name = optionalName {
    greeting = "Hello, \(name)"
}

switch 语句 #

goto 关键字 #

for 语句 #

case 1

let names = ["Anna", "Alex", "Brian", "Jack"]
for name in names {
    print("Hello, \(name)!")
}

case 2

for _ in 0..<10 {
    let newItem = Item(context: viewContext)
    newItem.timestamp = Date()
}

同样支持 continue 和 break

do-catch 语句与 try 关键字 #

在 Swift 中,try 关键字用于异常处理(也称为错误处理)。它与 do-catch 语句结合使用,以处理可能在运行过程中发生的错误。Swift 的错误处理模型与许多其他编程语言类似,通过抛出和捕获异常来处理错误情况。

  1. 抛出错误(Throwing Errors):定义哪些函数或方法可以抛出错误。
  2. 处理错误(Handling Errors):捕获并处理抛出的错误。

在 Swift 中,try 关键字用于调用可能抛出错误的函数或方法。它可以与 do-catch 语句结合使用,也可以单独使用。除了在 do-catch 语句中使用 try,Swift 还提供了 try?try! 两种特殊的 try 用法:

1. trydo-catch 结合使用 #

同时可以看到 throw 关键字的使用。

enum FileError: Error {
    case fileNotFound
    case unreadable
    case encodingFailed
}

func readFile(filename: String) throws -> String {
    // 模拟抛出错误
    throw FileError.fileNotFound
}

do {
    let content = try readFile(filename: "example.txt")
    print(content)
} catch FileError.fileNotFound {
    print("File not found.")
} catch FileError.unreadable {
    print("File is unreadable.")
} catch FileError.encodingFailed {
    print("Failed to encode the file.")
} catch {
    print("An unexpected error occurred: \(error).")
}

2. try? #

try? 将尝试执行可能抛出错误的表达式,如果抛出错误,则返回 nil,否则返回可选值。因此,它可以避免使用 do-catch 语句:

if let content = try? readFile(filename: "example.txt") {
    print(content)
} else {
    print("Failed to read the file.")
}

3. try! #

try! 将强制执行可能抛出错误的表达式,并假定不会抛出错误。如果错误确实抛出,程序将崩溃。应谨慎使用:

let content = try! readFile(filename: "example.txt")
print(content)

自定义错误类型 #

可以通过实现 Error 协议来定义自定义的错误类型。以下是一个定义和使用自定义错误类型的示例:

enum DivisionError: Error {
    case dividedByZero
}

func divide(_ num: Int, by divisor: Int) throws -> Int {
    if divisor == 0 {
        throw DivisionError.dividedByZero
    }
    return num / divisor
}

do {
    let result = try divide(10, by: 2)
    print("Result: \(result)")
} catch DivisionError.dividedByZero {
    print("Cannot divide by zero.")
} catch {
    print("An unexpected error occurred: \(error).")
}

guard-else 语句 #

guard 关键字是 Swift 中一个非常有用的控制语句,用于提前退出某个代码块,通常用于验证条件并确保程序流在有效状态下继续执行。如果条件不符合,guard 语句必须执行退出当前作用范围的操作,比如 returnbreakcontinuethrow

guard 的基本语法 #

guard condition else {
    // 如果条件不满足,则执行退出操作
    return  // 或其他退出操作
}

// 如果条件满足,继续执行下面的代码

使用场景 #

guard 关键字常用于以下几种情况:

  1. 条件验证: 用于检查函数参数或外部输入是否满足特定条件。
  2. 强制解包: 用于安全地解包可选值,并在解包失败时提前退出。
  3. 防御性编程: 提前处理错误或不期望的情况,使主代码路径更清晰。

使用示例 #

示例 1: 条件验证

func validateAge(age: Int) {
    guard age >= 18 else {
        print("You must be at least 18 years old.")
        return
    }
    print("Access granted.")
}

validateAge(age: 20)  // 输出: Access granted.
validateAge(age: 16)  // 输出: You must be at least 18 years old.

示例 2: 强制解包

func printUsername(user: [String: String]) {
    guard let username = user["username"] else {
        print("No username found.")
        return
    }
    print("Username is \(username).")
}

let user1 = ["username": "Alice"]
let user2 = ["email": "alice@example.com"]

printUsername(user: user1)  // 输出: Username is Alice.
printUsername(user: user2)  // 输出: No username found.

示例 3: 多个条件验证

func process(order: [String: Any]) {
    guard
        let productID = order["productID"] as? Int,
        let quantity = order["quantity"] as? Int,
        quantity > 0
    else {
        print("Invalid order.")
        return
    }
    print("Processing order for productID \(productID) with quantity \(quantity).")
}

let order1 = ["productID": 123, "quantity": 10]
let order2 = ["productID": "abc", "quantity": 0]

process(order: order1)  // 输出: Processing order for productID 123 with quantity 10.
process(order: order2)  // 输出: Invalid order.

使用注意事项 #

  • guard 必须退出: 在 else 块中必须执行退出操作,否则会引发编译错误。这保证了在条件不满足时程序的正确性。
  • 作用范围: 在 guard 语句中解包的值在验证通过后依然有效。这使得主代码路径更为简洁,更容易阅读和维护。

比较 guardif #

  • if 语句: 用于处理条件为真的情况。代码路径可能有多个分支,可能会导致嵌套和代码不易阅读。
  • guard 语句: 用于处理条件为假的情况,提前退出,有助于减少嵌套,使主代码路径更为直观和清晰。

示例对比

// 使用 if 嵌套
func processIf(order: [String: Any]) {
    if let productID = order["productID"] as? Int {
        if let quantity = order["quantity"] as? Int {
            if quantity > 0 {
                print("Processing order for productID \(productID) with quantity \(quantity).")
            } else {
                print("Invalid quantity.")
            }
        } else {
            print("Invalid quantity.")
        }
    } else {
        print("Invalid productID.")
    }
}

// 使用 guard 提前退出
func processGuard(order: [String: Any]) {
    guard
        let productID = order["productID"] as? Int,
        let quantity = order["quantity"] as? Int,
        quantity > 0
    else {
        print("Invalid order.")
        return
    }
    print("Processing order for productID \(productID) with quantity \(quantity).")
}

总之,guard 关键字的引入是为了提高代码的可读性和可维护性,使得你的代码结构更加清晰,减少不必要的嵌套,处理错误条件更加明确。

枚举 #

枚举(Enumeration)是 Swift 中的一种强大的数据类型,它允许你定义一组相关的值,并且每个值都被认为是同一类型的一部分。枚举可以帮助你组织代码,更好地表示和处理一组相关的值。

枚举的定义和使用 #

一个简单的枚举定义如下:

enum CompassPoint {
    case north
    case south
    case east
    case west
}

可以像这样声明一个枚举变量:

var direction = CompassPoint.north

你可以使用点语法来改变枚举的值:

direction = .east

关联值 #

枚举可以包含关联值,这使得枚举成员可以在其背后存储不同类型的关联值。关联值与枚举成员相关联,每个枚举成员的关联值类型可以各不相同。

enum Barcode {
    case upc(Int, Int, Int, Int)
    case qrCode(String)
}

var productBarcode = Barcode.upc(8, 85909, 51226, 3)
productBarcode = .qrCode("ABCDEFGHIJKLMNOP")

你可以使用 switch 语句来匹配枚举值并提取关联值:

switch productBarcode {
case .upc(let numberSystem, let manufacturer, let product, let check):
    print("UPC: \(numberSystem), \(manufacturer), \(product), \(check)")
case .qrCode(let productCode):
    print("QR Code: \(productCode)")
}

原始值 #

枚举成员可以被预填充默认值,称为原始值(raw values)。这些原始值的类型可以是字符串、字符或任何整型或浮点类型,每个原始值在其枚举声明中必须是唯一的。

enum Planet: Int {
    case mercury = 1, venus, earth, mars, jupiter, saturn, uranus, neptune
}

let earthsOrder = Planet.earth.rawValue // earthsOrder是3

你可以使用 rawValue 初始化一个枚举成员:

let possiblePlanet = Planet(rawValue: 3) // possiblePlanet 是 Planet.earth

枚举中的方法 #

枚举可以定义实例方法,方法可以访问枚举成员的值并运行一些逻辑:

enum CompassPoint {
    case north, south, east, west
    
    mutating func next() {
        switch self {
        case .north:
            self = .east
        case .east:
            self = .south
        case .south:
            self = .west
        case .west:
            self = .north
        }
    }
}

var direction = CompassPoint.north
direction.next()  // direction 现在是 .east

在这个例子中,next() 方法修改了 CompassPoint 枚举实例的值,并且因为它会修改实例,所以它被标记为 mutating

递归枚举 #

递归枚举是一种枚举类型,它有一个或多个枚举成员使用该类型的实例作为关联值。为了声明递归枚举,必须在枚举成员前使用 indirect 关键字。

enum ArithmeticExpression {
    case number(Int)
    indirect case addition(ArithmeticExpression, ArithmeticExpression)
    indirect case multiplication(ArithmeticExpression, ArithmeticExpression)
}

// 你也可以在枚举前加上 indirect 关键字
indirect enum ArithmeticExpression {
    case number(Int)
    case addition(ArithmeticExpression, ArithmeticExpression)
    case multiplication(ArithmeticExpression, ArithmeticExpression)
}

下面是如何使用递归枚举来定义和计算算术表达式的例子:

let five = ArithmeticExpression.number(5)
let four = ArithmeticExpression.number(4)
let sum = ArithmeticExpression.addition(five, four)
let product = ArithmeticExpression.multiplication(sum, ArithmeticExpression.number(2))

func evaluate(_ expression: ArithmeticExpression) -> Int {
    switch expression {
    case .number(let value):
        return value
    case .addition(let left, let right):
        return evaluate(left) + evaluate(right)
    case .multiplication(let left, let right):
        return evaluate(left) * evaluate(right)
    }
}

let result = evaluate(product) // result 是 18

使用注意 #

  1. 一旦确定变量的类型,再次为其赋值可以省略枚举类型名,使用更简短的点语法即可。
  2. 与其他语言的枚举不一样的是,swift 的枚举成员在创建时不会被赋予一个默认的整型值。
  3. switch 时 case 要覆盖全,或者用 default ,不然会报错。

小结 #

  • 定义枚举 使用 enum 关键字。
  • 基础用法:可以定义基本成员,并通过点语法访问和设置。
  • 关联值:允许枚举成员携带不同类型的值,增加灵活性。
  • 原始值:为枚举成员预定义原始值,可以是字符串、整型或浮点数。
  • 枚举方法:枚举可以包含方法,以增加行为逻辑。
  • 递归枚举:允许枚举成员包含自身类型的实例,用于构建复杂的数据结构。

枚举在 Swift 中是非常强大的工具,有助于创建更加严谨和类型安全的代码。

函数 #

Swift 函数(Functions)的使用

协议 Protocol #

Swift 中的协议(Protocol)的概念

结构体 #

Swift 中的结构体(Struct)概念

#

关键字 self #

在 Swift 中,关键字 self 有多种用途,主要用于引用当前实例、属性或方法。下面是 self 的几种常见用途:

1. 引用当前实例 #

  • 在类或结构体的方法中,可以使用 self 来引用当前实例。
class Person {
    var name: String
    
    init(name: String) {
        self.name = name
    }
    
    func greet() {
        print("Hello, my name is \(self.name)")
    }
}

2. 区分属性和参数 #

  • 当实例的属性名与参数名相同时,可以使用 self 来区分它们。
class Point {
    var x: Int
    var y: Int
    
    init(x: Int, y: Int) {
        self.x = x
        self.y = y
    }
    
    func setCoordinates(x: Int, y: Int) {
        self.x = x
        self.y = y
    }
}

3. 避免闭包循环引用 #

  • 在闭包内部引用当前实例时,可以使用 self 来避免循环引用。
class MyClass {
    var closure: (() -> Void)?
    
    func setupClosure() {
        self.closure = {
            self.doSomething()
        }
    }
    
    func doSomething() {
        print("Doing something")
    }
}

4. 在闭包中弱化引用 #

  • 在闭包中捕获 self 并使用弱引用来避免循环引用。
class SomeClass {
    var closure: (() -> Void)?
    
    func setupClosure() {
        self.closure = { [weak self] in
            self?.doSomething()
        }
    }
    
    func doSomething() {
        print("Doing something")
    }
}

总结 #

  • self 是一个特殊的关键字,用于引用当前实例、区分属性和参数、避免循环引用以及在闭包内部使用。
  • 在大多数情况下,Swift 会自动推断 self 的引用,因此在许多情况下,可以省略 self 关键字。

关键字 Self #

在 Swift 中,Self 是一个类型别名,用于引用当前类型。它主要用于泛型和协议中,能够使代码更具灵活性和复用性。

1. 协议中的 Self #

在协议中,Self 用于表示遵循该协议的具体类型。这意味着在定义协议时,可以使用 Self 来指代具体的实现类型。这对于定义返回自身类型的方法非常有用。

protocol Clonable {
    func clone() -> Self
}

class Person: Clonable {
    var name: String
    
    init(name: String) {
        self.name = name
    }
    
    func clone() -> Self {
        return Person(name: self.name) as! Self
    }
}

let original = Person(name: "John")
let clone = original.clone()
print(clone.name)  // 输出: John

在这个例子中,Clonable 协议定义了一个 clone 方法,返回类型为 Self。具体实现这个协议的类(如 Person)中的 clone 方法返回的实际类型也是 Person,保持了类型的一致性。

2. 类和结构体中的 Self #

在类和结构体中,Self 可用于表示当前类型,尤其在泛型和动态类型中使用。

class Vehicle {
    class func create() -> Self {
        return self.init()
    }
  
    required init() {
        // 初始化代码
    }
}

class Car: Vehicle {
    var model: String = "Sedan"
  
    required init() {
        super.init()
    }
}

let myCar = Car.create()
print(type(of: myCar))  // 输出: Car

在这个示例中,Vehicle 类有一个类方法 create,返回类型是 Self。通过调用 self.init()create 将返回调用它的具体类型的实例。

3. 泛型中的 Self #

在泛型上下文中,Self 能够创建与当前对象类型一致的返回类型。

protocol Initializable {
    init()
}

extension Initializable {
    static func createInstance() -> Self {
        return Self.init()
    }
}

struct MyStruct: Initializable {
    var value: Int = 10
}

let instance = MyStruct.createInstance()
print(type(of: instance))  // 输出: MyStruct

在这个例子中,Initializable 协议要求实现 init() 初始化方法,并在扩展中定义了一个返回类型为 Self 的静态方法 createInstance。最终,MyStruct 作为具体类型实现了该协议,并通过 createInstance 方法生成了与 MyStruct 类型一致的实例。

官方文档 #

你可以在 Swift 的官方文档中找到更多关于 Self 的内容:

访问控制 #

Access Control

  • open
  • public
  • internal
  • fileprivate
  • private

条件编译 #

通过条件编译来控制哪些代码在那些情况下需要编译,而哪些代码不需要。

#if <condition>
	举例:#if os(iOS) || os(watchOS) || os(tvOS)
#elseif <condition>

#else

#endif

其中 #elseif#else 是可选的。

Platform conditionValid arguments
os()macOSiOSwatchOStvOSvisionOSLinuxWindows
arch()i386x86_64armarm64
swift()>= or < followed by a version number
compiler()>= or < followed by a version number
canImport()A module name
targetEnvironment()simulatormacCatalyst

断言 #

assert 使用断言进行调试 #

只在 debug 下生效

precondition 强制先决条件 #

debug 和 release 都生效

泛型 #

泛型(Generics)是 Swift 语言中的强大特性,它允许你编写灵活且可重用的代码。通过使用泛型,你可以让函数、方法、类、结构体和枚举适用于任何类型,而不需要重复编写相同的代码。泛型有助于编写更加抽象和通用的代码,从而提高代码的复用性和类型安全性。

泛型在 Swift 中通过在尖括号 <T> 中声明类型参数来实现,以下是引入泛型的几个基本内容:

泛型函数 #

泛型函数允许你定义一个独立于任何特定类型的函数。例如,下面是一个非泛型的函数:

func swapTwoInts(_ a: inout Int, _ b: inout Int) {
    let temporaryA = a
    a = b
    b = temporaryA
}

使用泛型后,可以将函数改写为处理任意类型:

func swapTwoValues<T>(_ a: inout T, _ b: inout T) {
    let temporaryA = a
    a = b
    b = temporaryA
}

你可以使用这个泛型函数来交换任意类型的两个值:

var int1 = 1
var int2 = 2
swapTwoValues(&int1, &int2) // int1 现在是 2,int2 是 1

var str1 = "Hello"
var str2 = "World"
swapTwoValues(&str1, &str2) // str1 现在是 "World",str2 是 "Hello"

泛型类型 #

除了泛型函数,你还可以定义泛型类型,包括类、结构体和枚举。以下是一个使用泛型的栈(Stack)结构体示例:

struct Stack<Element> {
    private var items: [Element] = []

    mutating func push(_ item: Element) {
        items.append(item)
    }

    mutating func pop() -> Element? {
        return items.popLast()
    }

    func top() -> Element? {
        return items.last
    }

    var isEmpty: Bool {
        return items.isEmpty
    }
}

var intStack = Stack<Int>()
intStack.push(1)
intStack.push(2)
print(intStack.top()!) // 输出 2

var stringStack = Stack<String>()
stringStack.push("Hello")
stringStack.push("World")
print(stringStack.top()!) // 输出 "World"

泛型约束 #

你可以在泛型类型或函数上添加约束,要求类型参数必须符合特定的协议或父类。例如,下面是一个要求类型参数遵循 Equatable 协议的函数:

func findIndex<T: Equatable>(of valueToFind: T, in array: [T]) -> Int? {
    for (index, value) in array.enumerated() {
        if value == valueToFind {
            return index
        }
    }
    return nil
}

let numbers = [1, 2, 3, 4, 5]
if let index = findIndex(of: 3, in: numbers) {
    print("Index of 3 in numbers is \(index)") // 输出 "Index of 3 in numbers is 2"
}

如果要为泛型添加多个约束,可以使用 where 关键字:

func someFunction<T, U>(param1: T, param2: U) where T: Equatable, U: Comparable {  
    // 在这个函数中,T 需要遵循 Equatable 协议,U 需要遵循 Comparable 协议  
}

关联类型 #

协议可以有一个或多个关联类型作为其定义的一部分。关联类型是在协议内部定义的占位符名称,表示协议中使用到的某种类型,直到被采纳协议的类型具体化。例如:

protocol Container {
    associatedtype ItemType
    mutating func append(_ item: ItemType)
    var count: Int { get }
    subscript(i: Int) -> ItemType { get }
}

struct IntStack: Container {
    // existing implementation of IntStack
    var items = [Int]()
    mutating func push(_ item: Int) {
        items.append(item)
    }
    mutating func pop() -> Int {
        return items.removeLast()
    }

    // conformance to the Container protocol
    typealias ItemType = Int

    mutating func append(_ item: Int) {
        self.push(item)
    }

    var count: Int {
        return items.count
    }

    subscript(i: Int) -> Int {
        return items[i]
    }
}

总结 #

Swift 的泛型提供了一种强大且灵活的方式来编写可重用和类型安全的代码。通过泛型,你可以定义独立于任何具体类型的函数、方法、类、结构体和枚举,并在需要时进行类型约束,以确保代码的准确性和健壮性。理解和掌握泛型是编写高效、通用、高度可重用代码的关键。

宏(Macro) #

宏在编译时执行,可以在代码中生成或变换其他代码。宏在 Swift 语言中的应用能够帮助开发者在编译时进行一些高级的代码操作,从而使开发流程更加高效和简洁。

Preview #

在 Xcode 15 和 Swift 5.9 中,Apple 引入了这一宏语法,以简化视图预览的配置,让开发者能够以更直观和便捷的方式设置 SwiftUI 视图的预览。通过使用 #Preview 宏,开发者可以直接用这种方式创建视图的预览,而不需要像以前那样遵循更复杂的预览提供器(@PreviewProvider) 规范。这让代码更为简洁,同时提高了在 Xcode 中设计和调试用户界面的效率。

import SwiftUI

struct ContentView: View {
    var body: some View {
        Text("Hello, world!")
            .padding()
    }
}

#Preview {
    ContentView()
}

这个示例代码定义了一个简单的包含 “Hello, world!” 文本的视图,并使用 #Preview 来告诉 Xcode 在预览区域中展示这个视图。通过这种方式,开发者可以立即看到视图的外观并进行相应的调整,而不需编译和运行整个应用程序。

available #

if #available(iOS 14, *) {
    // 代码只在 iOS 14 及以上版本运行
} else {
    // 其他版本的替代实现代码
}

属性 Properties #

Swift 的计算属性概念

表达式 #

Swift 中的 key-path 表达式的概念与使用

关键字 #

Swift 中 mutating 关键字的使用

Swift 中 some 关键字的使用

Swift 中 as 关键字的使用

Swift 中 associatedtype 关键字的使用

Swift 中 typealias 关键字的使用

Swift 中 where 关键字的使用

Swift 中 extension 关键字的使用

@ 符号的使用 #

Swift 中 @ 符号的使用

$ 的使用 #

Swift 中 $ 美元符号的使用

本文共 8054 字,上次修改于 Nov 9, 2024
相关标签: Swift, Xcode, ByAI