swift Part 4

16 阅读8分钟

46 内存管理

跟OC一样,Swift也是采取基于引用计数的ARC内存管理方案(针对堆空间)

  1. 强引用(strongreference):默认情况下,引用都是强引用

    每次强引用使计数 +1,对象内存不会被提前释放。

  2. 弱引用(weakreference):通过 weak 定义弱引用

    必须是可选类型的var,因为实例销毁后,ARC会自动将弱引用设置为nil,ARC自动给弱引用设置nil时,不会触发属性观察器

  3. 无主引用(unownedreference):通过 unowned 定义无主引用

    不会产生强引用,实例销毁后仍然存储着实例的内存地址(类似于OC中的unsafe_unretained)试图在实例销毁后访问无主引用,会产生运行时错误(野指针)

// 内存泄漏

var fn: (()->())? // 声明函数 无参数, 无返回值

 func run() {
     print("跑起来了")
 }

 deinit{
     print("类销毁了~")
 }

override func viewDidLoad() {
    super.viewDidLoad()

    self.fn = { // self 强引用fn
        self.run() // fn 闭包强引用self
    }
    if let fnFunc = self.fn {
        fnFunc()
    }
}

// "跑起来了"
// weak self 解决内存泄漏
var fn: (()->())? // 声明函数 无参数, 无返回值
 func run() {
     print("跑起来了")
 }

 deinit{
     print("类销毁了~")
 }

override func viewDidLoad() {
    super.viewDidLoad()

    self.fn = {[weak self] in // self 强引用fn
        self?.run() fn 闭包弱引用 self
    }
    if let fnFunc = self.fn {
        fnFunc()
    }
}

// 跑起来了
// 类销毁了~

47 @escaping 非逃逸闭包 & 逃逸闭包

非逃逸闭包: 闭包调用在函数结束前, 函数作用域内调用

逃逸闭包: 闭包执行在函数结束后. 函数作用域外调用

ps: 注意: 逃逸闭包不可以捕获inout参数

47.1 非逃逸闭包

 func doWork(closure: () -> Void) {
        print("函数开始")
        closure()      // 闭包在函数返回前被调用
        print("函数结束")
 } 
// 调用

doWork {
    print("闭包执行中")
}

// 函数开始 
// 闭包执行中 
// 函数结束 

47.2 逃逸闭包


struct UserInfo {
    let name: String
    let email: String
    let token: String
}

class NetworkManager { 
    // 真实登录接口:闭包必须是 @escaping,因为网络请求是异步的
    func login(username: String,
               password: String,
               completion: @escaping (Result<UserInfo, Error>) -> Void) {
        
        // 模拟发起网络请求(实际这里是 URLSession.dataTask)
        DispatchQueue.global().asyncAfter(deadline: .now() + 1.5) {
            
            // 模拟服务器返回数据
            if username == "test@example.com" && password == "123456" {
                let user = UserInfo(name: "张三", email: username, token: "abc123xyz")
                completion(.success(user))
            } else {
                let error = NSError(domain: "LoginError", code: 401,
                                   userInfo: [NSLocalizedDescriptionKey: "账号或密码错误"])
                completion(.failure(error))
            }
        }
        
        // 注意:函数在这里就返回了,但上面的闭包要在 1.5 秒后才执行
        // 这就是为什么需要 @escaping
        print("登录请求已发出,函数返回")
    }
}
NetworkManager().login(username: "test@example.com", password: "123456") { result in
    print(result)
}

// 登录请求已发出,函数返回
// success(learn_swift1.UserInfo(name: "张三", email: "test@example.com", token: "abc123xyz"))

48 内存访问冲突

内存访问冲突会在两个访问满足下列条件时发生:

  1. 至少一个是写入操作
  2. 它们访问的是同一块内存
  3. 它们的访问时间重叠(比如在同一个函数内)
// 不存在内存访问冲突

func plus(_ num: inout Int) -> Int {
    num + 1
}


var number = 1 
number = plus(&number) // 正常输入输出参数, 做 + 1 操作
// 存在访问冲突

var step = 1

func increment(_ num: inout Int){
    num += step 
}

// Simultaneous accesses to 0x0, but modification requires exclusive access
// 同时访问0x0,但修改需要独占访问

increment(&step)

// num += step 同时调用了, step变量的setter方法 和getter方法 访问冲突
// 解决内存访问冲突

var copyOfStep = step
increment(&copyOfStep) // 修改copyOfStep 变量
step = copyOfStep // 重新赋值step

49 字面量协议

在 Swift 中,字面量 是指直接写在代码中的值,如数字、文本等,无需通过变量或常量计算得到

// 字面量协议

Bool : ExpressibleByBooleanLiteral  
Int : ExpressibleByIntegerLiteral  
FloatDouble : ExpressibleByIntegerLiteralExpressibleByFloatLiteral  
Dictionary : ExpressibleByDictionaryLiteral  
String : ExpressibleByStringLiteral  
ArraySet : ExpressibleByArrayLiteral  
Optional : ExpressibleByNilLiteral

// 结构体遵守 Int字面量协议 Float字面量协议
struct Money: ExpressibleByIntegerLiteral, ExpressibleByFloatLiteral {
    var cents: Int
    
    init(integerLiteral value: Int) {
        self.cents = value * 100
    }
    
    init(floatLiteral value: Double) {
        self.cents = Int(value * 100)
    }
}
// 直接用字面量创建
let price: Money = 19.99
let quantity: Money = 5

print(price.cents)     // 1999

// 遵守协议, 可以通过 Int Float 赋值的形式直接创建 结构体

50 匹配模式 (可选类型, where)

50.1 通配符模式 (Wildcard Pattern)

  1. _ 匹配: 任何值

  2. _? 匹配: 非nil值

// 通配符模式(Wildcard Pattern)
enum Life { // 定义枚举
     case human(name: String, age: Int?)
     case animal(name: String, age: Int?)
}

func check(_ life: Life) {
    
    switch life {
    case .human(let name, _): // 匹配任何值
        print("human", name)
        
    case .animal(let name, _?): // 不能为nil
        print("animal", name)
        
    default:
        print("other")
    }
}
check(.human(name: "Rose", age: 20)) // human Rose
check(.human(name: "Jack", age: nil)) // human Jack

check(.animal(name: "Dog", age: 5)) // animal Dog
check(.animal(name: "Cat", age: nil)) // other
// 注意最后返回other, age不能为nil, 所以执行default

50.2 值绑定模式(Value-Binding Pattern)

let point = (3, 2)
switch point {
    case let (x, y):
        print("The point is at(\(x),\(y)")  // 3,2分别绑定 x, y     
}       

50.3 元祖模式

// 元祖单匹配

    let points = [(0, 0), (1, 0), (2, 0)]
        for (x, _) in points {
          print(x)
    }

    // 0
    // 1
    // 2
// swith 三匹配

    let name: String? = "jack"
    let age = 18
    let info: Any = [1, 2]

    switch (name, age, info) {
      case (_?, _ , _ as String):
        print("case")
      default:
        print("default")
    }

    // default
//  遍历字典
    var scores = ["jack" : 98, "rose" : 100, "kate" : 86]
    
    for(key, value) in scores {
        print(key,value)
    }
    
   // rose 100 
   // kate 86 
   // jack 98

50.4 枚举case模式

let age = 2
// 1 原来的写法
if age >= 0 && age <= 9 {
   print("[0, 9]") 
}

// 2 枚举Case模式
if case 0...9 = age { // 匹配 age在0到9区间, 同上
   print("[0, 9]") 
}

// 3 guard
guard case 0...9 = age else { 
  return 
}
print("[0, 9]")

// 4 switch
switch age {
  case 0...9: 
    print("[0, 9]")
  default: 
    break
}

// 数组遍历
let ages: [Int?] = [2, 3, nil, 5]
for case nil in ages {
  print("有nil值")
  break
} // 有nil值

// 数组元祖
let points = [(1, 0), (2, 1), (3, 0)]
for case let (x, 0) in points {
   print(x)
} // 13

50.5 表达式模式

let point = (1, 2)
switch point {
  case (0, 0):
    print("(0, 0) is at the origin.")
  case (-2...2, -2...2):
    print("((point.0), (point.1)) is near the origin.")
  default:
    print("The point is at ((point.0), (point.1)).")
} 

// (1, 2) is near the origin

50.6 where条件

// 1 swift 中的 where

var data = (10, "Jack")

switch data {
  case let (age, _) where age > 10:
    print(data.1, "age > 10")
  case let (age, _) where age > 0:
    print(data.1, "age > 0")
  default: break
}

// 2 for 循环中的 where

var ages = [10, 20, 44, 23, 55]

for age in ages where age > 30 {
   print(age)
}  

// 44 55
// 3 协议中的where

protocol Stackable { 
  associatedtype Element // 关联类型
}

protocol Container {
  associatedtype Stack: Stackable where Stack.Element : Equatable
}
// 1 协议 Continer 内部的关联类型 Stack 需要遵守 Stackable 协议
// 2 关联类型 Stack 的关联类型 Element 需要遵守 Equatable 协议
// 方法中的 where

func equal<S1: Stackable, S2: Stackable>(_ s1: S1, _ s2: S2) -> Bool where S1.Element == S2.Element, S1.Element : Hashable {
   return false
}
// 扩展中的 where

extension Container where Self.Stack.Element : Hashable { }

46 函数式编程

函数式编程 = 用纯函数组合成数据管道,描述"做什么",而不是"怎么做"

// 假设你有一个班级的成绩单,需要完成以下任务:

// 1.  找出所有及格的成绩(≥60分)
// 2.  给每个及格成绩加5分(平时分奖励)
// 3.  计算奖励后的总分

let scores = [45, 78, 92, 33, 88, 67, 52, 91]
// 传统做法

// 命令式:一步步告诉计算机"怎么做"
var passingScores = [Int]()      // 1. 创建空数组
for score in scores {            // 2. 遍历每个分数
    if score >= 60 {             // 3. 判断是否及格
        passingScores.append(score)  // 4. 及格的加入数组
    }
}

var bonusScores = [Int]()        // 5. 创建另一个空数组
for score in passingScores {     // 6. 遍历及格分数
    bonusScores.append(score + 5)    // 7. 加5分后加入新数组
}

var total = 0                    // 8. 创建总分变量
for score in bonusScores {       // 9. 遍历加分后的分数
    total = total + score        // 10. 累加
}

print(total)  // 输出:78+97+93+72+96 = 436
// 实际计算:78(73+5)+97(92+5)+93(88+5)+72(67+5)+96(91+5) = 436
// 函数式编程

// 函数式:描述"要什么",让系统去"怎么做"
let total = scores
    .filter { $0 >= 60 }      // 1. 筛选及格
    .map { $0 + 5 }           // 2. 每人加5分
    .reduce(0, +)             // 3. 求和

print(total)  // 436

51 柯里化

柯里化(Currying)  是将一个接受多个参数的函数,转换成一系列每次只接受一个参数的函数链的技术。

柯里化的核心价值就是 复用

// 函数

func add(a: Int, b: Int) -> Int {
    return a + b
}
let result = add(a: 1, b: 2)  // 3
// 柯里化

func add(a: Int) -> (Int) -> Int { // 一个参数 返回个闭包
    return { b in
        return a + b
    }
}

// 使用方式1:分步调用
let addWith1 = add(a: 1)  // 得到一个函数 (Int) -> Int
let result = addWith1(2)   // 3

// 使用方式2:链式调用
let result2 = add(a: 1)(2)  // 3

52 面向协议编程

面向协议 = 定义能力(协议)+ 实现能力(类型)+ 统一使用(协议类型)

// 1. 定义协议 - 声明能力
protocol Flyable {
    func fly()
}

// 2. 让类型遵循协议
struct Bird: Flyable {
    func fly() {
        print("鸟在飞")
    }
}

struct Plane: Flyable {
    func fly() {
        print("飞机在飞")
    }
}

// 3. 使用协议
let flyingThings: [Flyable] = [Bird(), Plane()]
for thing in flyingThings {
    thing.fly()  // 鸟在飞  飞机在飞
}
// ❌ 继承的问题:只能继承一个父类
class Bird { func fly() {} }
class Plane { func fly() {} }
// 无法统一处理 Bird 和 Plane

// ✅ 协议的优点:关注能力,不关注类型
protocol Flyable { func fly() }
// Bird 和 Plane 都能飞,都能被统一处理

53 响应式编程

核心思想: 数据流动 + 变化自动传播 = 数据变了,依赖的地方自动更新

常用框架

  • Combine (Apple 官方)
  • RxSwift (第三方)
  • ReactiveSwift (第三方)