IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 移动开发 -> 闭包学习记录-iOS开发 -> 正文阅读

[移动开发]闭包学习记录-iOS开发

写在前面:在一开始JS基础学习的过程中,闭包的问题也曾困扰过我,其实无论是JS、OC、还是Swift,闭包的本质都是一样的。借着最近开始学习Swift,又重新将这部分复盘整理了一下。目前对于闭包相关的理解可以说是很透彻了,发现新问题再来补充。

专业的解释:

闭包(Closure)是词法闭包(Lexical Closure)的简称,是引用了自由变量的函数。这个被引用的自由变量将和这个函数一同存在,即使已经离开了创造它的环境也不例外。

形式:

Swift 中的闭包与 C 和 Objective-C 中的代码块(blocks)以及其他一些编程语言中的匿名函数(Lambdas)相似。

故名思义,闭合地包起来可以完成一件事儿就是个闭包。闭包这玩意儿,只是给它取了个名字而已,全局函数其实也是个闭包。还有闭包就是自包含的函数代码块,可以在代码中被传递和使用。比如,嵌套函数,函数作为参数的情况。

全局函数

嵌套函数

闭包表达式(轻量级语法)

有名字

有名字

匿名

不会捕获任何值

捕获其外部函数所有的参数和定义的常量和变量

捕获上下文中变量或常量的值

作用:

用来解决什么问题呢?闭包解决了函数内变量暴露给函数外访问的问题。可以缓存上级作用域,那么就使得函数外部打破了“函数作用域”的束缚。

值捕获,闭包可以在上下文中捕获常量或变量的引用。即使常量和变量的原作用域已经不存在,闭包仍然可以在闭包函数体内引用和修改这些值。👇这种情况就是值捕获

// 一般情况
// 闭包情况
func makeIncrementer(forIncrement amount: Int) -> () -> Int {
    var runningTotal = 0
    func incrementer() -> Int {
        runningTotal += amount
        return runningTotal
    }
    return incrementer
}

let incrementByTen = makeIncrementer(forIncrement: 10)

incrementByTen()
// 返回的值为10
incrementByTen()
// 返回的值为20
incrementByTen()
// 返回的值为30

let incrementBySeven = makeIncrementer(forIncrement: 7)
incrementBySeven()
// 返回的值为7

incrementByTen()
// 返回的值为40
incrementBySeven()
// 返回的值为17

接下来,我们来对👆🌰进行值捕获的解释👇

func incrementer() -> Int {
    runningTotal += amount
    return runningTotal
}

「👆incrementer() 函数并没有任何参数,但是在函数体内访问了 runningTotalamount 变量。这是因为它从外围函数捕获了 runningTotalamount 变量的引用。捕获引用保证了 runningTotalamount 变量在调用完 makeIncrementer 后不会消失,并且在下一次执行 incrementer 函数时,runningTotal 依旧存在。」

形式补充:

函数和闭包都是引用类型,将函数或闭包赋值给一个常量还是变量,实际上是将函数或闭包的引用设置为常量或变量的值,而并非闭包内容本身。意味着如果将闭包赋值给了两个不同的常量或变量,两个值都会指向同一个闭包。继续👆例子🌰

let alsoIncrementByTen = incrementByTen
alsoIncrementByTen()
// 返回的值为50

另外关于??闭包引起的循环强引用问题(遇到了再说吧)

语法:

函数和返回值类型都写在大括号内

闭包的函数体部分由关键字 in 引入

{ (parameters) -> type in
? ? statements
}

func backward(_ s1: String, _ s2: String) -> Bool {
? ? return s1 > s2
}

{ (s1: String, s2: String) -> Bool in
? ? return s1 > s2
}

语法优化:

1.省略参数和返回值类型,可根据上下文推断出

{ s1, s2 in return s1 > s2 }

2.行表达式闭包可以通过省略 return 关键字

{ s1, s2 in s1 > s2 }

3.参数名称缩写,$0 和 $1 表示闭包中第一个和第二个参数

{ $0 > $1 }

4.运算符方法【?这个暂时先保留,知识结构补充ing】

与 sorted(by:) 方法的参数需要的函数类型相符合。因此,你可以简单地传递一个大于号,Swift 可以自动推断找到系统自带的那个字符串函数的实现:reversedNames = names.sorted(by: >)

5.尾随闭包:

将闭包表达式写在函数圆括号之后,作为函数的最后一个参数调用。用于闭包长度非常长的情况

reversedNames = names.sorted() { $0 > $1 }

若闭包表达式是函数的唯一参数,可以把 () 省略掉

reversedNames = names.sorted { $0 > $1 }

应用场景:

1.逃逸闭包:当一个闭包作为一个函数的参数,且闭包在函数返回之后才被执行。定义函数时,在参数名之前标注 @escaping指明这个闭包允许“逃逸”出这个函数。

🌰不想复制粘贴,具体例子移步官方文档?逃逸闭包 - SwiftGG

相信看完文档中的🌰对这个概念已经有了大概的理解,我来浅浅总结一下。很多启动异步操作的函数接受一个闭包参数作为 completion handler。这类函数会在异步操作开始之后立刻返回但不被调用,直到异步操作结束后才会被调用。在这种情况下,闭包需要“逃逸”出函数,因为闭包需要在函数返回之后被调用。

补充:逃逸闭包需要显示引用self

项目中的🌰:实现 确认是否安装外部鱼眼镜头的 Alert弹窗

//? 这里isUpdateAvailable是VersionUtils的一个属性

func isUpdateAvailable(callback: @escaping (Bool) -> Void) {
    callback(true)
    return
}

isUpdateAvailable(callback: { isNew in
    if isNew {
        let alertVC = UpdateVersionAlertVC.shared
        alertVC.modalPresentationStyle = .overCurrentContext
        alertVC.modalTransitionStyle = .crossDissolve
        self.present(UpdateVersionAlertVC.shared, animated: true)
    }
}


//这里 把completed赋值给了实例 alertVC的completed属性,在alertVC的一个方法中对alertVC的completed进行传参调用,此时就需要标注@escaping(闭包需要在函数返回之后被调用)

extension FishShotAlertVC {
? ? @objc static func showFishShotAlertViewController(currentVC vc: UIViewController, completed: @escaping (_ isUseFishShot: Bool) -> Void) {
? ? ? ? let alertVC = FishShotAlertVC()
? ? ? ? alertVC.completed = completed
? ? ? ? alertVC.currentVc = vc
? ? ? ? vc.present(alertVC, animated: false, completion: {})
? ? }
}

补充:

当闭包作为函数的参数,该闭包也有参数时

1.声明函数时,不考虑闭包的具体内容,{ }里面是函数体,函数体内有对闭包进行传参调用。

2.函数调用时,作为尾随闭包,{ }里面是尾随闭包

写在最后:

学习闭包经历了三个阶段,文档看懂,写的时候语法忘了,

总而言之就是,以为懂了实际没懂,遇到实际场景返回来继续复盘,和别人讨论,直到可以回答别人提出的任何相关问题,不能解释就继续去学习。这个概念我应该算是彻彻底底懂了吧。

  移动开发 最新文章
Vue3装载axios和element-ui
android adb cmd
【xcode】Xcode常用快捷键与技巧
Android开发中的线程池使用
Java 和 Android 的 Base64
Android 测试文字编码格式
微信小程序支付
安卓权限记录
知乎之自动养号
【Android Jetpack】DataStore
上一篇文章      下一篇文章      查看所有文章
加:2022-08-19 19:17:25  更:2022-08-19 19:17:46 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/25 4:21:11-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码