惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
B
Blog RSS Feed
宝玉的分享
宝玉的分享
腾讯CDC
博客园_首页
T
Tailwind CSS Blog
月光博客
月光博客
博客园 - 司徒正美
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
M
MIT News - Artificial intelligence
A
About on SuperTechFans
云风的 BLOG
云风的 BLOG
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
有赞技术团队
有赞技术团队
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
大猫的无限游戏
大猫的无限游戏
MongoDB | Blog
MongoDB | Blog
博客园 - 聂微东
V
Visual Studio Blog
H
Hackread – Cybersecurity News, Data Breaches, AI and More
SecWiki News
SecWiki News
美团技术团队
P
Privacy International News Feed
H
Help Net Security
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
Microsoft Security Blog
Microsoft Security Blog
Know Your Adversary
Know Your Adversary
Y
Y Combinator Blog
D
DataBreaches.Net
Project Zero
Project Zero
T
The Blog of Author Tim Ferriss
Cyberwarzone
Cyberwarzone
C
Cybersecurity and Infrastructure Security Agency CISA
C
Cisco Blogs
S
Schneier on Security
G
GRAHAM CLULEY
博客园 - 三生石上(FineUI控件)
Cisco Talos Blog
Cisco Talos Blog
小众软件
小众软件
Forbes - Security
Forbes - Security
D
Docker
T
Tenable Blog
S
Secure Thoughts
雷峰网
雷峰网
S
Security @ Cisco Blogs
T
The Exploit Database - CXSecurity.com
The Cloudflare Blog
博客园 - 【当耐特】
Spread Privacy
Spread Privacy
阮一峰的网络日志
阮一峰的网络日志

Oragekk's Blog

OpenSpec 技术分享:用规格驱动 AI 编程 ChatGPT&Codex订阅教程 一个 waline 评论系统bug引发的思考 Jenkins 远程触发构建踩坑记 Git SSH 密钥配置 初识Rust vuepress-plugin-meting2 谷歌发布多平台应用开发神器Project IDX!PaLM 2加持 Vue常见优化手段 Vue2响应式原理解析 Dart 中的并发 Flutter 工作原理 如何利用GitHub Action提交URL到搜索引擎 提交URL到搜索引擎(百度、Bing、Google) GitHub Actions 使用介绍 素材设计 前端-Q&A 浏览器的事件循环 你不知道的 CSS 之包含块 CSS 属性计算过程 Vercel deploy忽略指定分支 评论插件 Waline 之邮件通知配置 公开API 终端究极美化iTerm2+Pure 使用Bing API提交网站URL Flutter 基础大集合 关于本站 关于本站 关于我 Markdown 展示 使用n命令管理node版本 幻灯片页 ReactNative State(状态) ReactNative开发环境配置,ES6语法介绍 ReactNative介绍 更优雅强大的终端ZSH 神经网络模型训练 YYCache优秀的缓存设计 WebViewJavascriptBridge NSError WCDB漫谈 优雅的实现TableViewCell单选 初探机器学习框架CoreML ijkPlayer 集成 iOS 配置https iOS timelineLogistics iOS Cookie的配置及使用 WKWebView拦截URL WKWebView使用及自适应高度 textfield限制输入字符 评论系统从多说迁移到disqus指南 利用Runtime进行快速归档 iOS 10.3 keychain 重大更新 Cell的accessoryType属性标记单元格之后,出现的重用问题 通过UserAgent判断设备 AFNetworking A memory leak 一人一句宋词 OC 中的枚举类型 iOS - Image compression algorithm iOS程序启动原理(下) iOS程序启动原理(上) NSOperatioin TableView性能优化 Runloop Test Three ways to call Update Cocoapods 1.1.1 减小iOS-App或者静态库体积 Jekyll旧站回忆 JavaScript ES6 CommonJS,RequireJS,SeaJS 归纳笔记 Unix/Linux 扫盲笔记
深入理解swift中闭包的捕捉语义
Oragekk · 2017-10-19 · via Oragekk's Blog

参考链接:losures Capture Semantics, Part 1: Catch them all!

概述

原文中先定义一个 Pokemon 类

class Pokemon: CustomDebugStringConvertible {
  let name: String
  init(name: String) {
    self.name = name
  }
  var debugDescription: String { return "\(name)>" }
  deinit { print("\(self) escaped!") }
}

延时执行的闭包

func delay(seconds: Int, closure: ()->()) {
  let time = DispatchTime.now() + .seconds(seconds)
  DispatchQueue.main.after(when: time) {
    print("🕑")
    closure()
  }
}

默认捕捉语法

在 swift 中默认的捕捉语法是:被捕获的变量在闭包被执行的时候才被定值1
. 我们能说它捕获到了这个变量的引用(或者  指针)。

func demo1() {
  let pokemon = Pokemon(name: "Mewtwo")
  print("before closure: \(pokemon)")
  delay(1) {
    print("inside closure: \(pokemon)")
  }
  print("bye")
}

这个闭包会在 demo1() 方法函数执行完成后 1 秒后被执行,并且我们已退出了方法函数的作用域… 当然 Pokemon 仍然是存在的,当这个代码块在下一个 1 秒后再次被执行的时候!

before closure: <Pokemon Mewtwo>
bye
🕑
inside closure: <Pokemon Mewtwo>
<Pokemon Mewtwo> escaped!

在这个例子中,当这个闭包被 GCD 执行后,这个闭包自行释放,就是 Pokemon 内部的 init 方法执行的时候。
此处 Swift 自动捕获到这个 pokemon 变量,当 demo1 方法执行完成并且释放掉的时候,1s 之后闭包被执行的时候还是捕获到了 pokemon

被捕获到的变量都被执行的时候定值

var name = "John"

let test1 = {
    print(name)
}

name = "Aby"

test1()

此处在创建了 test1 闭包之后,我们改变了 name 的值,打印结果是 Aby。说明此处 Swift 捕获到的是 name 这个变量的引用(指针)

原文中的例子使用了方法,并做了延时,大意在说明,函数被释放之后的延时闭包中,闭包依然捕获了变量的新值,而不是旧值,说明此处它捕获的是变量的引用,而非变量本身的值

这个在值类型中也可行例如 Int

修改变量的值

如果捕获的值是一个  var 并不是一个  let,你还是可以修改这个值  在闭包内部2

var name = "John"

let test1 = {
    name = "Jack"
    print(name)
}

name = "Aby"

test1()

此处打印结果为 Jack,再一次印证,闭包内捕获的是变量 name 的引用,因为它可以被改变,而不是一个静态的拷贝

捕获一个作为静态 copy 的变量

var str = "Hello, playground"
let show1 = { [strcopy = str] in
// 在闭包一开始创建的时候捕获变量的值,
// []内的为捕获列表,一开始捕获值,而非引用。
// 捕获的为原始变量的副本->常量,并且只能在闭包内访问

    print("这是str-----\(str)\n这是strcopy-----\(strcopy)")
}

str = "hello"

show1()
这是str-----hello
这是strcopy-----Hello, playground

如果你想要在闭包创建的时候捕获变量的值,而不是仅仅当闭包执行的时候去获取它的定值,你能使用一个捕获列表
捕获列表可以被编码在方括号的中间,在闭包开括号的右边(并且在闭包的参数 / 或者有返回值之前)3

为了在闭包创建的时候,捕获变量的值(而不是这个变量本身的引用),你可以使用  [形参 = 实参]

你也可以这样写,不过我觉得不够清晰

var str = "Hello, playground"
let show1 = { [str] in
    print(str)
}

这里的捕获列表大概相当于文中的如下代码

func demo6_equivalent() {
  var pokemon = Pokemon(name: "Pikachu")
  print("before closure: \(pokemon)")
  // here we create an intermediate variable to hold the instance
  // pointed by the variable at that point in the code:
  let pokemonCopy = pokemon
  delay(1) {
    print("inside closure: \(pokemonCopy)")
  }
  pokemon = Pokemon(name: "Mewtwo")
  print("after closure: \(pokemon)")
}

这就好像,如果我们创建一个中间变量去指向同一个 pokemon,并且捕获这个变量
事实上,使用这个捕获列表和上面的代码一样… 除了这个 pokemonCopy 的中间变量是闭包的局部变量,并且将只能在闭包内被访问。

总结

  • Swift 闭包捕获了一个对外部变量需要在闭包内部使用的一个引用。
  • 那个引用在闭包被执行的时候获得定值。
  • 作为对这个变量的引用的捕捉(并且不是这个变量自身),你能从闭包内部修改这个变量的值(当然,如果这个变量被声明为 var 并且不是 let)
  • 相反,你能告诉 Swfit 在闭包创建的时候对这个变量定值 并且把这个值保存在本地的一个静态变量中,而不是捕获变量本身。你可以通过使用捕获列表(中括号),在括号内表达。

注意:因为捕获列表是一个列表你还可以放多个参数,用逗号隔开,像数组一样

最后感谢原文作者