🎬 HoRain 云小助手个人主页

⛺️生活的理想,就是为了理想的生活!


⛳️ 推荐

前些天发现了一个超棒的服务器购买网站,性价比超高,大内存超划算!忍不住分享一下给大家。点击跳转到网站。

目录

⛳️ 推荐

Swift 闭包详解

1. 闭包的基本概念

闭包表达式语法

简单示例

2. 闭包的简化语法

示例:排序数组

3. 尾随闭包

4. 闭包捕获值

5. 闭包类型

闭包作为函数参数

闭包作为返回值

闭包作为属性

6. 逃逸闭包和非逃逸闭包

非逃逸闭包(默认)

逃逸闭包

使用逃逸闭包的场景

7. 自动闭包

8. 闭包中的内存管理

循环引用问题

解决方案:捕获列表

9. 高级闭包用法

函数柯里化

闭包作为类型别名

闭包组合

10. 实际应用示例

网络请求

动画

高阶函数

最佳实践


Swift 闭包详解

闭包是 Swift 中强大且灵活的功能,类似于匿名函数或 Lambda 表达式。

1. 闭包的基本概念

闭包表达式语法

{ (parameters) -> returnType in
    statements
}

简单示例

// 完整写法
let greet = { (name: String) -> String in
    return "Hello, \(name)!"
}
print(greet("Alice"))  // Hello, Alice!

// 简化写法
let greetShort: (String) -> String = { name in
    "Hello, \(name)!"
}

2. 闭包的简化语法

示例:排序数组

let names = ["Chris", "Alex", "Ewa", "Barry", "Daniella"]

// 原始写法
let sorted1 = names.sorted(by: { (s1: String, s2: String) -> Bool in
    return s1 > s2
})

// 类型推断
let sorted2 = names.sorted(by: { s1, s2 in return s1 > s2 })

// 隐式返回(单表达式)
let sorted3 = names.sorted(by: { s1, s2 in s1 > s2 })

// 简写参数名
let sorted4 = names.sorted(by: { $0 > $1 })

// 运算符方法
let sorted5 = names.sorted(by: >)

// 尾随闭包
let sorted6 = names.sorted { $0 > $1 }

3. 尾随闭包

当闭包是函数的最后一个参数时,可以写成尾随闭包。

// 标准写法
func loadData(completion: (String) -> Void) {
    completion("Data loaded")
}
loadData(completion: { data in
    print(data)
})

// 尾随闭包写法
loadData { data in
    print(data)
}

// 多个参数,最后一个闭包
func fetchData(id: Int, completion: (String) -> Void) {
    completion("Data for \(id)")
}

fetchData(id: 1) { data in
    print(data)
}

4. 闭包捕获值

闭包可以捕获和存储其所在上下文中的常量和变量。

func makeIncrementer(forIncrement amount: Int) -> () -> Int {
    var runningTotal = 0
    
    // 闭包捕获了 runningTotal 和 amount
    let incrementer: () -> Int = {
        runningTotal += amount
        return runningTotal
    }
    
    return incrementer
}

let incrementByTen = makeIncrementer(forIncrement: 10)
incrementByTen()  // 返回 10
incrementByTen()  // 返回 20
incrementByTen()  // 返回 30

let incrementByFive = makeIncrementer(forIncrement: 5)
incrementByFive()  // 返回 5
incrementByFive()  // 返回 10

5. 闭包类型

闭包作为函数参数

func travel(action: (String) -> Void) {
    print("I'm getting ready to go.")
    action("London")
    print("I arrived!")
}

// 使用
travel { (place: String) in
    print("I'm going to \(place)")
}

闭包作为返回值

func createTravel() -> (String) -> Void {
    var counter = 1
    
    return { destination in
        print("\(counter). I'm going to \(destination)")
        counter += 1
    }
}

let travel = createTravel()
travel("Paris")  // 1. I'm going to Paris
travel("Tokyo")  // 2. I'm going to Tokyo

闭包作为属性

class DataManager {
    var onDataLoaded: ((String) -> Void)?
    
    func loadData() {
        // 模拟异步加载
        DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
            self.onDataLoaded?("Data loaded successfully")
        }
    }
}

let manager = DataManager()
manager.onDataLoaded = { message in
    print(message)
}
manager.loadData()

6. 逃逸闭包和非逃逸闭包

非逃逸闭包(默认)

func processNumbers(numbers: [Int], transform: (Int) -> Int) -> [Int] {
    return numbers.map(transform)
}

// 闭包在函数返回前就执行完了
let result = processNumbers(numbers: [1, 2, 3]) { $0 * 2 }

逃逸闭包

闭包在函数返回后被执行,需要使用 @escaping标注。

class DataLoader {
    var completionHandlers: [() -> Void] = []
    
    func loadData(completion: @escaping () -> Void) {
        // 存储闭包,稍后执行
        completionHandlers.append(completion)
    }
    
    func executeCompletions() {
        for handler in completionHandlers {
            handler()
        }
        completionHandlers.removeAll()
    }
}

使用逃逸闭包的场景

func fetchData(completion: @escaping (String) -> Void) {
    // 模拟异步网络请求
    DispatchQueue.global().async {
        // 模拟网络延迟
        Thread.sleep(forTimeInterval: 2)
        
        DispatchQueue.main.async {
            completion("Data received")
        }
    }
}

fetchData { data in
    print(data)  // 2秒后打印
}

7. 自动闭包

自动闭包自动将表达式包装成闭包,延迟求值。

var customersInLine = ["Chris", "Alex", "Ewa", "Barry", "Daniella"]
print(customersInLine.count)  // 5

// 创建一个自动闭包
let customerProvider = { customersInLine.remove(at: 0) }
print(customersInLine.count)  // 5(还未执行)

print(customerProvider())  // Chris
print(customersInLine.count)  // 4

// 使用 @autoclosure
func serve(customer customerProvider: @autoclosure () -> String) {
    print("Now serving \(customerProvider())!")
}

serve(customer: customersInLine.remove(at: 0))  // Alex

8. 闭包中的内存管理

循环引用问题

class MyClass {
    var value = 0
    var closure: (() -> Void)?
    
    func setupClosure() {
        // 强引用循环
        closure = {
            self.value += 1  // 闭包捕获了self
        }
    }
    
    deinit {
        print("MyClass deinitialized")
    }
}

// 测试
var obj: MyClass? = MyClass()
obj?.setupClosure()
obj = nil  // 不会打印 "deinitialized",内存泄漏!

解决方案:捕获列表

class MyClass {
    var value = 0
    var closure: (() -> Void)?
    
    func setupClosure() {
        // 1. 弱引用
        closure = { [weak self] in
            guard let self = self else { return }
            self.value += 1
        }
        
        // 2. 无主引用(适用于确定不会为nil的情况)
        // closure = { [unowned self] in
        //     self.value += 1
        // }
        
        // 3. 捕获特定属性
        closure = { [self] in
            value += 1
        }
    }
    
    deinit {
        print("MyClass deinitialized")
    }
}

9. 高级闭包用法

函数柯里化

func addNumbers(_ a: Int) -> (Int) -> Int {
    return { b in
        return a + b
    }
}

let addFive = addNumbers(5)
print(addFive(3))  // 8
print(addFive(10)) // 15

闭包作为类型别名

typealias CompletionHandler = (Result<String, Error>) -> Void
typealias Transformer<T> = (T) -> T

func process<T>(value: T, transform: Transformer<T>) -> T {
    return transform(value)
}

let result = process(value: 5) { $0 * 2 }  // 10

闭包组合

func compose<A, B, C>(
    _ f: @escaping (A) -> B,
    _ g: @escaping (B) -> C
) -> (A) -> C {
    return { a in
        g(f(a))
    }
}

let addTwo = { $0 + 2 }
let multiplyByThree = { $0 * 3 }
let addThenMultiply = compose(addTwo, multiplyByThree)

print(addThenMultiply(5))  // 21

10. 实际应用示例

网络请求

func fetchData(from url: String, completion: @escaping (Result<Data, Error>) -> Void) {
    guard let url = URL(string: url) else {
        completion(.failure(NSError(domain: "Invalid URL", code: 400)))
        return
    }
    
    URLSession.shared.dataTask(with: url) { data, response, error in
        if let error = error {
            completion(.failure(error))
            return
        }
        
        guard let data = data else {
            completion(.failure(NSError(domain: "No data", code: 404)))
            return
        }
        
        completion(.success(data))
    }.resume()
}

动画

UIView.animate(withDuration: 0.3, animations: {
    // 动画代码
}) { finished in
    // 动画完成后的回调
    if finished {
        print("Animation completed")
    }
}

高阶函数

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

// map
let doubled = numbers.map { $0 * 2 }  // [2, 4, 6, 8, 10]

// filter
let evens = numbers.filter { $0 % 2 == 0 }  // [2, 4]

// reduce
let sum = numbers.reduce(0) { $0 + $1 }  // 15

// sorted
let sorted = numbers.sorted { $0 > $1 }  // [5, 4, 3, 2, 1]

// forEach
numbers.forEach { print($0) }

最佳实践

  1. 简化语法:在可能的情况下使用简写参数名

  2. 避免循环引用:使用 [weak self][unowned self]

  3. 适当逃逸:只在需要时使用 @escaping

  4. 保持简洁:避免闭包过长,复杂的逻辑应提取为单独函数

  5. 类型安全:利用 Swift 的类型推断,但也要保持可读性

  6. 错误处理:在异步闭包中妥善处理错误

  7. 性能考虑:避免在闭包中创建不必要的对象

闭包是 Swift 中函数式编程的核心,合理使用可以使代码更简洁、灵活和强大。

❤️❤️❤️本人水平有限,如有纰漏,欢迎各位大佬评论批评指正!😄😄😄

💘💘💘如果觉得这篇文对你有帮助的话,也请给个点赞、收藏下吧,非常感谢!👍 👍 👍

🔥🔥🔥Stay Hungry Stay Foolish 道阻且长,行则将至,让我们一起加油吧!🌙🌙🌙

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐