返回

Swift中的逃逸闭包(@escaping)与非逃逸闭包(@noescaping)

IOS

什么是逃逸闭包和非逃逸闭包?

逃逸闭包 :一个接受闭包作为参数的函数,该闭包可能在函数返回后才被调用,也就是说这个闭包逃离了函数的作用域,这种闭包称为逃逸闭包。当你声明一个接受闭包作为形式参数的函数时,你可以在形式参数前写@escaping来明确闭包是允许逃逸。例如:当网络请求结束后调用的闭包。发起请求后过了一段时间,闭包可能在函数返回后才被调用。

非逃逸闭包 :一个闭包在函数或方法返回后不会再被调用,它只能在函数或方法返回之前执行。当函数或方法返回时,闭包将被销毁,不再存在。使用@noescaping修饰符可以显式声明该闭包是非逃逸闭包,这可以帮助编译器进行优化,提高程序的性能。例如:在一个循环中,每个元素的处理逻辑可以使用非逃逸闭包来实现。

逃逸闭包和非逃逸闭包的区别

特征 逃逸闭包 非逃逸闭包
定义 可以离开函数或方法的作用域而继续执行的闭包 只能在函数或方法的作用域内执行的闭包
@escaping @noescaping
内存管理 需要手动管理内存 由编译器自动管理内存
循环引用 可能产生循环引用 不产生循环引用
用途 异步编程、网络请求、定时器等 数组、字典的遍历、字符串处理等

逃逸闭包的使用

逃逸闭包通常用于异步编程和网络请求等场景。例如,在一个异步请求的场景中,当请求结束后,我们需要执行某个操作。此时,我们需要使用逃逸闭包来将这个操作传递给异步请求的函数,以便在请求结束后执行这个操作。

func asynchronousRequest(completion: @escaping (Result<Data, Error>) -> Void) {
    // 发起异步请求
    // 请求结束后执行completion闭包
}

asynchronousRequest { result in
    switch result {
    case .success(let data):
        // 处理请求成功的结果
    case .failure(let error):
        // 处理请求失败的结果
    }
}

在上面的代码中,asynchronousRequest函数接受一个逃逸闭包completion作为参数,这个闭包将在请求结束后执行。completion闭包的类型是(Result<Data, Error>) -> Void,这意味着它接受一个Result<Data, Error>类型的参数,并返回一个Void类型的结果。

非逃逸闭包的使用

非逃逸闭包通常用于数组、字典的遍历、字符串处理等场景。例如,在一个数组的遍历场景中,我们需要对数组中的每个元素执行某个操作。此时,我们可以使用非逃逸闭包来将这个操作传递给遍历函数,以便在遍历过程中对每个元素执行这个操作。

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

numbers.forEach { number in
    print(number)
}

在上面的代码中,forEach函数接受一个非逃逸闭包作为参数,这个闭包将在遍历数组中的每个元素时执行。forEach闭包的类型是(Element) -> Void,这意味着它接受一个Element类型的参数,并返回一个Void类型的结果。

逃逸闭包和非逃逸闭包的内存管理

逃逸闭包需要手动管理内存,因为它们可以在函数或方法返回后继续执行。如果逃逸闭包引用了函数或方法中的局部变量,那么这些局部变量将被保留在内存中,直到逃逸闭包执行结束。这可能会导致内存泄漏。

非逃逸闭包由编译器自动管理内存,因为它们只能在函数或方法的作用域内执行。当函数或方法返回时,非逃逸闭包将被销毁,不再存在。因此,非逃逸闭包不会产生内存泄漏。

逃逸闭包和非逃逸闭包的循环引用

逃逸闭包可能产生循环引用,因为它们可以在函数或方法返回后继续执行。如果逃逸闭包引用了函数或方法中的局部变量,那么函数或方法将引用逃逸闭包。这样,就会形成一个循环引用,导致内存泄漏。

非逃逸闭包不产生循环引用,因为它们只能在函数或方法的作用域内执行。当函数或方法返回时,非逃逸闭包将被销毁,不再存在。因此,非逃逸闭包不会产生循环引用。

逃逸闭包和非逃逸闭包的总结

逃逸闭包和非逃逸闭包是Swift中闭包的两种不同类型。逃逸闭包可以在函数或方法返回后继续执行,而非逃逸闭包只能在函数或方法返回之前执行。逃逸闭包需要手动管理内存,而非逃逸闭包由编译器自动管理内存。逃逸闭包可能产生循环引用,而非逃逸闭包不产生循环引用。

在使用闭包时,需要根据具体情况选择使用逃逸闭包还是非逃逸闭包。一般来说,如果闭包将在函数或方法返回后继续执行,那么就应该使用逃逸闭包。否则,就应该使用非逃逸闭包。