swift_041(Swift的@noescape和@escaping)

article/2025/10/17 11:45:55


@noescapeswift3.0中已经被废弃,在swift3.0@noescape被用作一个默认值。

@escaping属性写在参数类型的前面而不是参数名称的前面。这是swift3里一个新的点。


这里需要先介绍一下escape的概念。当一个闭包当做一个参数传进函数里,这个闭包是在这个函数执行完后执行的,这个时候我们就说这个闭包从函数逃出来了(escape)。这种场景很常见,比如我们进行一个异步的请求,请求时会传入一个handler,比如当请求成功后执行达到回调的目的。


众所周知swift的内存管理是引用计数。闭包里用到的数据都需要捕捉到闭包里,保证闭包执行时这些数据不会被释放还在内存里。Xcode为了让我们意识到闭包里用到的对象其实已经被retain了,就要求我们访问当前属性时显示声明self。这个时候如果新手就很容易犯引用循环的错误。闭包retainselfself如果又持有retain了闭包。最后就谁都释放不了,内存就泄露了。


swift里针对非escape@noescape表示。

比如map函数就使用了:

funcmap<T>(@noescape transform: (Self.Generator.Element)throws ->T)rethrows -> [T]


这样标记之后能看到的好处就是这个闭包里如果再使用self的属性不需要加self.了。对于编译器而言,在知道是noescape闭包后可以进行一些内存的优化。


非逃逸闭包(@noescape

如果这个闭包是在这个函数结束前内被调用,就是非逃逸的即noescape


逃逸闭包(@escaping)(Escaping Closure

如果一个闭包被作为一个参数传递给一个函数,并且在函数return之后才被唤起执行,那么这个闭包是逃逸闭包。如果这个闭包是在函数执行完后才被调用,调用的地方超过了这函数的范围,所以叫逃逸闭包@escaping


举个例子就是我们常用的masonry或者snapkit的添加约束的方法就是非逃逸的。因为这闭包马上就执行了。

public funcsnp_makeConstraints(file: String = #file, line: UInt = #line, @noescape closure: (make: ConstraintMaker) ->Void) -> Void {

ConstraintMaker.makeConstraints(view:self, file: file, line: line, closure: closure)

}


网络请求请求结束后的回调的闭包则是逃逸的,因为发起请求后过了一段时间后这个闭包才执行。比如这个Alamofire里的处理返回jsoncompletionHandler闭包,就是逃逸的。


publicfunc responseJSON(

        queue queue: dispatch_queue_t? = nil,

  options: NSJSONReadingOptions = .AllowFragments,

        completionHandler: Response<AnyObject, NSError> -> Void)

-> Self 

{

return response(

            queue: queue,

  responseSerializer:Request.JSONResponseSerializer(options: options),

            completionHandler: completionHandler

)

}

很多人在写闭包参数的时候总是忽略去判断这个闭包是否是逃逸的。这对闭包的内存管理优化不太友好,都被当做了逃逸闭包处理。所以在3中做出了一个对调的改变:所有的闭包都默认为非逃逸闭包,不再需要@noescape;如果是逃逸闭包,就用@escaping表示。比如下面的一段代码,callBack在函数执行完后1秒才执行,所以是逃逸闭包。

func startRequest(callBack: ()->Void ) {

DispatchQueue.global().asyncAfter(deadline:DispatchTime.now() + 1) { 

callBack()

}

}

这样就需要显示的声明@escaping才能编译通过。



重点

这里几点关于创建默认不可逃逸闭包的好处:最明显的好处就是编译器优化你的代码的性能和能力。如果编译器知道这个闭包是不可逃逸的,它可以关注内存管理的关键细节。

而且你可以在不可逃逸闭包里放心的使用self关键字,因为这个闭包总是在函数return之前执行,你不需要去使用一个弱引用去引用self.这对你而言是一个非常nice的功能。


参考:http://blog.csdn.net/yydev/article/details/52683329


  • @escaping

Swift3的行为更好。因为它默认是安全的:如果一个函数参数可能导致引用循环,那么它需要被显示地标记出来。@escaping标记可以作为一个警告,来提醒使用这个函数的开发者注意引用关系。非逃逸闭包可用被编译器高度优化,快速的执行路径将被作为基准而使用,除非你在有需要的时候显式地使用其他方法。


func doWork(block:()->()) {

    print("header")

    block()

    print("footer")

}

doWork {

    print("work")

}

//控制台打印的消息如下:

//header

//work

//footer


对于上述的block调用是同步行为。我们修改一下代码,将block放到一个异步操作中,让它在doWork返回后被调用。这个时候我们就需要用@escaping标记表明这个闭包是会逃逸的。


func doWorkAsync(block: @escaping () -> ()) {

    DispatchQueue.main.async { 

        block()

    }

}

没有逃逸的闭包的作用域是不会超过函数本身的,所以说我们不需要担心在闭包内持有self。逃逸的闭包就不同了,因为需要确保闭包内的成员依然有效,如果在闭包内引用self以及self的成员的话,就要考虑闭包内持有self的情况了。

class S {

    var foo = "foo"


    func method1() {

        doWork {

            print(foo)

        }

        foo = "bar"

    }


    func method2() {

        doWorkAsync {

            print(self.foo)

        }

        foo = "bar"

    }

func method3() {

        doWorkAsync {

            [weak self] _ in

            print(self?.foo)

        }

        foo = "bar"

    }


}

S().method1()// foo

S().method2()// bar

S().method3()// nil

method1不需要考虑self .foo的持有情况,而method2需要考虑,我们让闭包持有了self,打印的值就是foo赋值之后的内容bar,如果我们不希望闭包内持有self的话,可以使用[weak self]的方法来表示. method3就是这样,在闭包执行的时候已经没有了对实例对象的引用,所有说输出是nil

  • weak unowned
  • 上面我用的是 [weak self],如果用[unowned self]表示的话,method3就需要稍做修改:funcmethod3() {
  •       doWorkAsync {
  •           [unowned self] _ in
  •           print(self.foo)
  •       }
  •       foo = "bar"
  •   }
    这两者都是用来防止循环引用的
  • unowned有点像oc里面的unsafe_unretained,而weak就是以前的weak
  • 对于这两者的使用,要视情况而定。用unowned的话,即使它原来的引用的内容被释放了,它仍然会保持对被已经释放了的对象的一个引用,它不能是Optional也不能是nil值,这个时候就会出现一个问题,如果你调用这个引用方法或者访问成员属性的话,就会出现崩溃。
  • weak要稍微友善一点,在引用的内容被释放之后,会自动将weak的成员标记为nil。有人要说,既然这样,那我全部使用weak。但是在可能的情况下,我们还是应该倾向于尽量减少出现Optional的可能性,这样有助于代码的简化。Apple给我们的建议是如果能够确定访问时不会被释放的话,尽量用unowned,如果存在被释放的可能性的话,就用weak


  • S().method1()// foo
  • S().method2()// bar
  • S().method3()// nil
    参考资料:http://stackoverflow.com/questions/24058336/how-do-i-run-asynchronous-callbacks-in-playground






http://chatgpt.dhexx.cn/article/9QcZwhak.shtml

相关文章

swift函数参数指针传递inout和@escaping冲突的解决方法

这里写目录标题 需求解决方法容易产生的问题总结 今天深入解决关于函数参数指针的问题。问题是这样&#xff0c;我在swift的一个函数里面&#xff0c;参数里需要传递一个指针类型的Int&#xff0c;但是这个函数里面还有一个逃逸闭包escaping,于是就会报错这样&#xff1a;Escap…

Using the “escape“ directive (legacy escaping) is not allowed when auto-escaping is on with a markup

springboot 2.3.0版本以上&#xff0c;springboot自动引用freemarker模板文件的后缀从.ftl变成了.ftlh <!DOCTYPE html> <#escape x as x?html> <#include "../common/macro.ftl"> <html lang"en"> </html> </#escape&…

Escaping closure captures non-escaping parameter ‘xx‘

新版的Swift闭包做参数默认是noescaping&#xff0c;不再是escaping。 如果函数里执行该闭包&#xff0c;要添加escaping。

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

逃逸闭包 概念&#xff1a;一个接受闭包作为参数的函数&#xff0c;该闭包可能在函数返回后才被调用&#xff0c;也就是说这个闭包逃离了函数的作用域&#xff0c;这种闭包称为逃逸闭包。当你声明一个接受闭包作为形式参数的函数时&#xff0c;你可以在形式参数前写escaping来…

VMware创建Linux虚拟机之(三)Hadoop安装与配置及搭建集群

Hello&#xff0c;world&#xff01; &#x1f412;本篇博客使用到的工具有&#xff1a;VMware16 &#xff0c;Xftp7 若不熟悉操作命令&#xff0c;推荐使用带GUI页面的CentOS7虚拟机 我将使用带GUI页面的虚拟机演示 虚拟机&#xff08;Virtual Machine&#xff09; 指通过…

hadoop安装及简单的使用

hadoop安装及简单的使用 一、hadoop运行环境搭建二、hadoop 目录结构三、hadoop 本地模式四、hadoop 伪分布式模式五、伪分布式 YARN 运行六、伪分布式启动历史服务器七、伪分布式配置日志的聚集八、hadoop 集群模式 一、hadoop运行环境搭建 1.环境准备 安装 hadoop 需要使用 …

hadoop安装(window10)

一、下载和winutils 1.下载&#xff1a;http://archive.apache.org/dist/hadoop/core/ 官网下载&#xff1a;http://hadoop.apache.org/releases.html &#xff08;提供最新的几个版本&#xff09; https://github.com/steveloughran/winutils&#xff08;windows安装需要&…

Hadoop安装与配置

第一步&#xff1a;装虚拟机 我把hadoop安装需要的文件放在下面大家自行去取&#xff1a; 百度网盘 请输入提取码 提取码&#xff1a;8888 由于centos镜像文件太大传不上去需要的话可以加我qq:364960241 目录 第一步&#xff1a;装虚拟机 第二步&#xff1a;配置静态网络…

基于CentOS虚拟机的Hadoop安装教程(自用备忘)

该博文是用于记录Hadoop的安装过程&#xff0c;且记录其中出现的一些问题&#xff0c;防止日后遗忘 实验环境&#xff1a; 虚拟机&#xff1a;CentOS7Hadoop&#xff1a;3.3.2java&#xff1a;java 8u331下载连接&#xff1a; CentOS&#xff1a;centos-7-x86_64-dvd-2009.iso…

Hadoop安装与配置详细教程

【确保服务器集群安装和配置已经完成&#xff01;】 前言 请根据读者的自身情况&#xff0c;进行相应随机应变。 我的三台CentOS7服务器&#xff1a; 主机&#xff1a;master&#xff08;192.168.56.110&#xff09; 从机&#xff1a;slave0&#xff08;192.168.56.111&…

Hadoop安装教程 Linux版

Hadoop安装教程 Linux版 一、Linux虚拟机安装 方法一&#xff1a;使用Windows下Linux子系统&#xff08;大佬可选&#xff09; 方法二&#xff1a;使用VMware安装Linux虚拟机&#xff08;小白可选&#xff09; 方法三&#xff1a;安装双系统&#xff08;不怕麻烦的可以试试&am…

Ubuntu下的Hadoop安装

二、Hadoop安装 采用版本为hadoop-2.7.7 ①JDK的安装 2.1.1 创建文件夹 sudo mkdir /expt sudo chmod 777 /expt 这个出错我参考别的原因也改过来了&#xff0c; pkexec chmod 0440 /etc/sudoers 2.1.2 移动文件 之前已经解压过了&#xff0c;所以改一下位置就行了 2.1.3 创…

hadoop 安装与测试

1、下载安装包 登录官网hadoop.apache.org 下载安装包 版本可以根据自己的需求进行选择 本文需要下载的安装包是&#xff1a;Hadoop-3.1.3.tar.gz / jdk-14_linux-x64_bin.tar.gz 将Windows上的安装包上传到Linux上&#xff0c;方式有很多。本文是将安装包通过共享文件夹形式…

Hadoop安装目录

/root/software/hadoop-2.6.0-cdh5.15.1/etc/hadoop第一步骤 vi core-site.xml <configuration> <property> <name>fs.defaultFS</name> <value>hdfs://hadoop000:8020</value> </property> <property> <name>hadoop.t…

hadoop安装常见问题

目录 SSH服务器拒绝了密码。请再试一次​ ubuntu修改登陆用户名 这样ftp成功链接ubuntu 现在的ubuntu支持直接拖拽文件传输&#xff0c;不用ftp的 yarn是什么 linux配置java环境变量 hadoop安装包下载 linux不能编辑sshd_cofig SSH的简单理解和运用 SSH服务器拒绝了密…

hadoop安装流程

这篇文章主要记录安装hadoop的过程&#xff0c;给后来者一点帮助&#xff0c; 官网获取安装包 选一款你想要的 Apache Hadoop 下载完成后上传到服务器&#xff08;我用的是finalshell&#xff09; 先别急着解压 准备Java包 像上面一样官网下载对应的Java包 这边我已经准…

hadoop安装

hadoop安装 安装模板虚拟机 1) 设置虚拟机IP地址 2) 设置windows虚拟地址 3) 修改OS的IP地址 [roothadoop100 ~]# vim /etc/sysconfig/network-scripts/ifcfg-ens33BOOTPROTOstatic IPADDR # 自己的ip GATEWAY # 自己的网关地址 DNS1 # 自己的dns地址# 一定不要有空格!不要有…

Hadoop 安装与配置

创建虚拟机 这里需要下载ubuntu操作系统 创建一个hadoop用户&#xff0c;并使用/bin/bash 作为shell 虚拟机打开成功后&#xff0c;打开终端Terminal&#xff0c;开始创建hadoop用户 sudo useradd -m hadoop -s /bin/bash 为hadoop设置一个密码 sudo passwd hadoop 为后续操作方…

Hadoop 安装部署

官网下载软件&#xff1a; Ubuntu : https://releases.ubuntu.com/ jdk &#xff1a; https://www.oracle.com/java/technologies/downloads/ Hadoop &#xff1a; https://archive.apache.org/dist/hadoop/common/hadoop-3.3.2/ 下载后上传到服务器&#xff0c;解压 一 1. …

Hadoop的安装与配置(非常重要)

官方的原生配置文档Hadoop3.1.0 HDFS的组成 NameNodesecondaryNameNodeDataNode 这是以主从模式来运行的&#xff0c;前两个在maser节点上&#xff0c;最后一个在slave节点上 1. 解压hadoop安装包 要学会从官方网站里找到历史镜像和release的发布版本来下载 我觉得从别人给的…