iOS开发-Swift进阶之闭包,使用&捕获原理!

最新iOS开发常见面试题总结一!(附答案) 1.iOS 类(class)和结构体(struct)有什么区别? Swift 中,类是引用类型,结构体是值类型。值类型在传递和赋值时将进行复制,而引用类型则只会使用引用对象的一个"指向"。所以他们两者之间的区别就是两个类型的区别。 举个简单的例子,代码如下 class Temperature { var value: Float = 37.0 } class Person { var temp: Temperature? func sick() { temp?.value = 41. 阅读详情

swift进阶总汇

本文主要分析闭包以及闭包捕获变量的原理

闭包

闭包是一个捕获了全局上下文的常量或者变量的函数,通俗来讲,闭包可以是常量也可以是函数

  • 【全局函数是一种特殊的闭包】:定义一个全局函数,只是当前的全局函数并不捕获值
func test(){
    print("test")
}

  • 【函数闭包】:下面的函数是一个闭包,函数中的incrementer是一个内嵌函数,可以从makeIncrementer中捕获变量runningTotal
func makeIncrementer() -> () -> Int{
    var runningTotal = 10
    //内嵌函数,也是一个闭包
    func incrementer() -> Int{
        runningTotal += 1
        return runningTotal
    }
    return incrementer
}

  • 【闭包表达式 / 匿名函数】:下面是一个闭包表达式,即一个匿名函数,而且是从上下文中捕获变量和常量
//闭包表达式
{ (param) -> ReturnType in
    //方法体
}

使用闭包的好处

  • 1、利用上下文推断参数和返回值类型

  • 2、单表达式可以隐式返回,即省略return关键字

  • 3、参数名称的简写,例如 $0表示第一个参数

  • 4、尾随闭包表达式

闭包表达式

OC与swift的对比

  • OC中的Block其实是一个匿名函数,需要具备以下特点:

    • 1、作用域 {}

    • 2、参数和返回值

    • 3、函数体(in)之后的代码

  • swift中的闭包,可以当做变量,也可以当做参数传递

var clourse: (Int)->(Int) = { (age: Int) in
    return age
}

闭包表达式的使用方式

作为一个开发者,有一个学习的氛围跟一个交流圈子特别重要,这是一个我的iOS开发交流群:130 595 548,不管你是小白还是大牛都欢迎入驻 ,让我们一起进步,共同发展!(群内会免费提供一些群主收藏的免费学习书籍资料以及整理好的几百道面试题和答案文档!)

  • **【可选类型的闭包表达式】**1、将闭包表达式声明成一个可选类型
//声明一个可选类型的闭包
<!--错误写法-->
var clourse: (Int) -> Int?
clourse = nil

<!--正确写法-->
var clourse: ((Int) -> Int)?
clourse = nil

  • **【闭包常量】**2、通过let将闭包声明成一个常量(即一旦赋值之后就不能更改)
//2、通过let将闭包声明为一个常量,即一旦赋值后就不能改变了
let clourse: (Int) -> Int
clourse = {(age: Int) in
    return age
}
//报错:Immutable value 'clourse' may only be initialized once
clourse = {(age: Int) in
    return age
}

  • **【闭包参数】**3、将闭包作为 函数的参数
//3、将闭包作为函数的参数
func test(param: () -> Int){
    print(param())
}
var age = 10
test { () -> Int in
    age += 1
    return age
}

尾随闭包

当闭包作为函数的最后一个参数,如果当前的闭包表达式很长,我们可以通过尾随闭包的书写方法来提高代码的可读性

//闭包表达式作为函数的最后一个参数
func test(_ a: Int, _ b: Int, _ c: Int, by: (_ item1: Int, _ item2: Int, _ item3: Int) -> Bool) -> Bool{
    return by(a, b, c)

}
//常规写法
test(10, 20, 30, by: {(_ item1: Int, _ item2: Int, _ item3: Int) -> Bool in
        return (item1 + item2 < item3)
})
//尾随闭包写法
test(10, 20, 30) { (item1, item2, item3) -> Bool in
    return (item1 + item2 < item3)
}

  • 我们平常使用的array.sorted其实就是一个尾随闭包,且这个函数就只有一个参数,如下所示
//array.sorted就是一个尾随闭包
var array = [1, 2, 3]
//1、完整写法
array.sorted { (item1: Int, item2: Int) -> Bool in return item1 < item2}
//2、省略参数类型:通过array中的参数推断类型
array.sorted { (item1, item2) -> Bool in return item1 < item2}
//3、省略参数类型 + 返回值类型:通过return推断返回值类型
array.sorted { (item1, item2) in return item1 < item2}
//4、省略参数类型 + 返回值类型 + return关键字:单表达式可以隐士表达,即省略return关键字
array.sorted { (item1, item2) in item1 < item2}
//5、参数名称简写
array.sorted {return $0 < $1}
//6、参数名称简写 + 省略return关键字
array.sorted {$0 < $1}
//7、最简:直接传比较符号
array.sorted (by: <)

捕获一个变量

下面代码的打印结果是什么?

func makeIncrementer() -> () -> Int{
    var runningTotal = 10
    //内嵌函数,也是一个闭包
    func incrementer() -> Int{
        runningTotal += 1
        return runningTotal
    }
    return incrementer
}
let makeInc = makeIncrementer()
print(makeInc())
print(makeInc())
print(makeInc())

<!--打印结果-->
11
12
13

打印结果如下,从结果中可以看出,每次的结构都是在上次函数执行的基础上累加的,但是我们所知的runningTotal是一个临时变量,按理说每次进入函数都是10,这里为什么会每次累加呢? 主要原因:内嵌函数捕获了runningTotal,不再是单纯的一个变量了

  • 如果是下面这种方式调用呢?
print(makeIncrementer()())
print(makeIncrementer()())
print(makeIncrementer()())

<!--打印结果-->
11
11
11

为什么这种方式每次打印的结果就是同一个呢?

1、SIL分析

将上述代码通过SIL分析:

  • 1、通过alloc_box申请了一个堆上的引用计数,并将引用计数地址给了RunningTotal,将变量存储到堆上

  • 2、通过project_box从堆上取出变量

  • 3、将取出的变量交给闭包进行调用

    结论:所以,捕获值的本质是 将变量存储到堆上

2、断点验证

  • 也可以通过断点来验证,在makeIncrementer方法内部调用了swift_allocObject方法

总结

  • 一个闭包能够从上下文捕获已经定义的常量和变量,即使这些定义的常量和变量的原作用域不存在,闭包仍然能够在其函数体内引用和修改这些值

  • 当每次修改捕获值时,修改的是堆区中的value值

  • 当每次重新执行当前函数时,都会重新创建内存空间

所以上面的案例中我们知道:

  • makeInc是用于存储makeIncrementer函数调用的全局变量,所以每次都需要依赖上一次的结果

  • 而直接调用函数时,相当于每次都新建一个堆内存,所以每次的结果都是不关联的,即每次结果都是一致的

闭包是引用类型

这里还要一个疑问,makeInc存储的到底是什么?个人猜测存储的是runningTotal的堆区地址,下面我们通过分析来验证是否如此

但是此时我们发现,通过SIL并没有办法分析出什么,那么可以将SIL降一级,通过IR代码来观察数据的构成

在分析之前,首先来了解下IR的基本语法

IR基本语法
  • 通过以下命令将代码转换为IR文件
swiftc -emit-ir 文件名 > ./main.ll && code main.ll

例如:
- cd 文件所在路径
- swiftc -emit-ir main.swift > ./main.ll && open main.ll

  • 数组
/*
- elementnumber 数组中存放数据的数量
- elementtype 数组中存放数据的类型
*/
[<elementnumber> x <elementtype>]

<!--举例-->
/*
24个i8都是0
- iN:表示多少位的整型,即8位的整型 - 1字节
*/
alloca [24 x i8], align 8

  • 结构体
/*
- T:结构体名称
- <type list> :列表,即结构体的成员列表
*/
//和C语言的结构体类似
%T = type {<type list>}

<!--举例-->
/*
- swift.refcounted:结构体名称
- %swift.type*:swift.type指针类型
- i64:64位整型 - 8字节
*/
%swift.refcounted = type { %swift.type*, i64}

  • 指针类型
<type> *

<!--举例-->
//64位的整型 - 8字节
i64*

  • getelementptr指令
    在LLVM中获取数组和结构体的成员时通过getelementptr,语法规则如下:
<result> = getelementptr <ty>, <ty>* <ptrval>{, [inrange] <ty> <id x>}*

<result> = getelementptr inbounds <ty>, <ty>* <ptrval>{, [inrange] <ty> <idx>}*

<!--举例-->
struct munger_struct{
    int f1;
    int f2;
};
void munge(struct munger_struct *P){
    P[0].f1 = P[1].f1 + P[2].f2;
}

//使用
struct munger_struct* array[3];

int main(int argc, const char * argv[]) {

    munge(array);

    return 0;
}

通过下面的命令将c/c++编译成IR

clang -S -emit-llvm 文件名 > ./main.ll && code main.ll

<!--举例-->
clang -S -emit-llvm ${SRCROOT}/06-EnumTestC/main.c > ./main.ll && code main.ll

  • 第一个索引:%struct.munger_struct* %13, i32 0 等价于 第一个索引类型 + 第一个索引值 ==》 共同决定 第一个索引的偏移量
  • 第二个索引:i32 0

再结合图来理解

int main(int argc, const char * argv[]) { 
    int array[4] = {1, 2, 3, 4}; 
    int a = array[0];
    return 0;
}
其中int a = array[0];这句对应的LLVM代码应该是这样的:
/*
- [4 x i32]* array:数组首地址
- 第一个0:相对于数组自身的偏移,即偏移0字节 0 * 4字节
- 第二个0:相对于数组元素的偏移,即结构体第一个成员变量 0 * 4字节
*/
a = getelementptr inbounds [4 x i32], [4 x i32]* array, i64 0, i64 0

  • 可以看到其中的第一个0,使用基本类型[4 x i32],因此返回的指针前进0 * 16字节,即当前数组首地址

  • 第二个index,使用基本类型 i32,返回的指针前进0字节,即当前数组的第一个元素,返回的指针类型是 i32*

总结

  • 第一个索引不会改变返回的指针的类型,即ptrval前面的*对应什么类型,返回的就是什么类型

  • 第一个索引的偏移量是由第一个索引的值和第一个ty指定的基本类型共同确定的

  • 后面的索引是在数组或者结构体内进行索引

  • 每增加一个索引,就会使得该索引使用的基本类型和返回的指针类型去掉一层(例如 [4 x i32] 去掉一层是 i32)

IR分析

分析IR代码

  • 查看makeIncrementer方法
    • 1、首先通过swift_allocObject创建swift.refcounted结构体
    • 2、然后将swift.refcounted转换为<{ %swift.refcounted, [8 x i8] }>*结构体(即Box)
    • 3、取出结构体中index等于1的成员变量,存储到[8 x i8]*连续的内存空间中
    • 4、将内嵌函数的地址存储到i8即void地址中
    • 5、最后返回一个结构体

其结构体定义如下

仿写

通过上述的分析,仿写其内部的结构体,然后构造一个函数的结构体,将makeInc的地址绑定到结构体中

struct HeapObject {
    var type: UnsafeRawPointer
    var refCount1: UInt32
    var refCount2: UInt32
}

//函数返回值结构体
//BoxType 是一个泛型,最终是由传入的Box决定的
struct FunctionData<BoxType>{
    //内嵌函数地址
    var ptr: UnsafeRawPointer
    var captureValue: UnsafePointer<BoxType>
}

//捕获值的结构体
struct Box<T> {
    var refCounted: HeapObject
    var value: T
}

//封装闭包的结构体,目的是为了使返回值不受影响
struct VoidIntFun {
    var f: () ->Int
}

//下面代码的打印结果是什么?
func makeIncrementer() -> () -> Int{
    var runningTotal = 10
    //内嵌函数,也是一个闭包
    func incrementer() -> Int{
        runningTotal += 1
        return runningTotal
    }
    return incrementer
}
let makeInc = VoidIntFun(f: makeIncrementer())

let ptr = UnsafeMutablePointer<VoidIntFun>.allocate(capacity: 1)
//初始化的内存空间
ptr.initialize(to: makeInc)
//将ptr重新绑定内存
let ctx = ptr.withMemoryRebound(to: FunctionData<Box<Int>>.self, capacity: 1) {
     $0.pointee
}
print(ctx.ptr)
print(ctx.captureValue.pointee)

<!--打印结果-->
0x0000000100002bc0
Box<Int>(refCounted: _7_Clourse.HeapObject(type: 0x0000000100004038, refCount1: 3, refCount2: 2), value: 10)

  • 终端命令查找0000000100002bc0(其中0x0000000100002bc0 是内嵌函数的地址)
nm -p /Users/chenjialin/Library/Developer/Xcode/DerivedData/07、Clourse-bsccpnlhsrkbzkdglsojfgisewnx/Build/Products/Debug/07、Clourse | grep 0000000100002bc0

其中s10_7_Clourse15makeIncrementerSiycyF11incrementerL_SiyFTA是内嵌函数的地址对应的符号

结论:所以当我们var makeInc2 = makeIncrementer()使用时,相当于给makeInc2就是FunctionData结构体,其中关联了内嵌函数地址,以及捕获变量的地址,所以才能在上一个的基础上进行累加

捕获两个变量的情况

上面的案例中,我们分析了闭包捕获一个变量的情况,如果是将捕获一个变量更改为捕获两个变量呢?如下所示修改makeIncrementer函数

func makeIncrementer(forIncrement amount: Int) -> () -> Int{
    var runningTotal = 0
    //内嵌函数,也是一个闭包
    func incrementer() -> Int{
        runningTotal += amount
        return runningTotal
    }
    return incrementer
}

  • 查看其IR代码

内部结构仿写

根据捕获一个变量的仿写,继续仿写捕获两个变量的情况

//2、闭包捕获多个值的原理
struct HeapObject {
    var type: UnsafeRawPointer
    var refCount1: UInt32
    var refCount2: UInt32
}

//函数返回值结构体
//BoxType 是一个泛型,最终是由传入的Box决定的
struct FunctionData<BoxType>{
    var ptr: UnsafeRawPointer//内嵌函数地址
    var captureValue: UnsafePointer<BoxType>
}

//捕获值的结构体
struct Box<T> {
    var refCounted: HeapObject
    var value: T
}

//封装闭包的结构体,目的是为了使返回值不受影响
struct VoidIntFun {
    var f: () ->Int
}

//下面代码的打印结果是什么?
func makeIncrementer(forIncrement amount: Int) -> () -> Int{
    var runningTotal = 0
    //内嵌函数,也是一个闭包
    func incrementer() -> Int{
        runningTotal += amount
        return runningTotal
    }
    return incrementer
}
var makeInc = makeIncrementer(forIncrement: 10)
var f = VoidIntFun(f: makeInc)

let ptr = UnsafeMutablePointer<VoidIntFun>.allocate(capacity: 1)
//初始化的内存空间
ptr.initialize(to: f)
//将ptr重新绑定内存
let ctx = ptr.withMemoryRebound(to: FunctionData<Box<Int>>.self, capacity: 1) {
     $0.pointee
}
print(ctx.ptr)
print(ctx.captureValue)

<!--打印结果-->
0x0000000100002910
0x00000001040098e0

  • 通过终端命令查看第一个地址是否是内嵌函数的地址

    注:(函数必须使用VoidIntFun包装下,否则转换后的地址不是内嵌函数的地址),如下所示

  • 通过cat查看 第一个地址,即内嵌函数的地址

    • x/8g 第二个地址

    • 继续查看内存情况

如果将runningTotal改成12呢?来验证是否如我们猜想的一样。事实证明,确实是存储的runningTotal

所以,闭包捕获两个变量时,Box结构体内部发生了变化,修改后的仿写代码如下:

//2、闭包捕获多个值的原理
struct HeapObject {
    var type: UnsafeRawPointer
    var refCount1: UInt32
    var refCount2: UInt32
}

//函数返回值结构体
//BoxType 是一个泛型,最终是由传入的Box决定的
struct FunctionData<BoxType>{
    var ptr: UnsafeRawPointer//内嵌函数地址
    var captureValue: UnsafePointer<BoxType>
}

//捕获值的结构体
struct Box<T> {
    var refCounted: HeapObject
    //valueBox用于存储Box类型
    var valueBox: UnsafeRawPointer
    var value: T
}

//封装闭包的结构体,目的是为了使返回值不受影响
struct VoidIntFun {
    var f: () ->Int
}

//下面代码的打印结果是什么?
func makeIncrementer(forIncrement amount: Int) -> () -> Int{
    var runningTotal = 12
    //内嵌函数,也是一个闭包
    func incrementer() -> Int{
        runningTotal += amount
        return runningTotal
    }
    return incrementer
}

var makeInc = makeIncrementer(forIncrement: 10)
var f = VoidIntFun(f: makeInc)

let ptr = UnsafeMutablePointer<VoidIntFun>.allocate(capacity: 1)
//初始化的内存空间
ptr.initialize(to: f)
//将ptr重新绑定内存
let ctx = ptr.withMemoryRebound(to: FunctionData<Box<Int, Int>>.self, capacity: 1) {
     $0.pointee
}
print(ctx.ptr)
print(ctx.captureValue.pointee)
print(ctx.captureValue.pointee.valueBox)

<!--打印结果-->
0x0000000100002b30
Box<Int>(refCounted: _7_Clourse.HeapObject(type: 0x0000000100004090, refCount1: 3, refCount2: 4), valueBox: 0x00000001006094a0, value: 10)
0x00000001006094a0

疑问:如果是捕获3个变量呢?

  • 如下所示,是捕获三个值的内存情况

  • 通过IR文件发现,从返回值倒推

<!--返回值-->
ret { i8*, %swift.refcounted* } %15

<!--%15-->
%15 = insertvalue { i8*, %swift.refcounted* }
{ i8* bitcast (i64 (%swift.refcounted*)* @"$s4main15makeIncrementer12forIncrement7amount2SiycSi_SitF11incrementerL_SiyFTA" to i8*),
    %swift.refcounted* undef }, %swift.refcounted* %10, 1

<!--%10-->
//与捕获两个变量相比,区别在于 i64 32 变成了 i64 40
%10 = call noalias %swift.refcounted* @swift_allocObject(
%swift.type* getelementptr inbounds (%swift.full_boxmetadata, %swift.full_boxmetadata* @metadata.3, i32 0, i32 2),
i64 40, i64 7) #1

所以Box结构体改为

//捕获值的结构体
struct Box<T> {
    var refCounted: HeapObject
    //这也是一个HeapObject
    var valueBox: UnsafeRawPointer
    var value1: T
    var value2: T
}

最终完整的仿写代码为

//3、捕获3个值
struct HeapObject {
    var type: UnsafeRawPointer
    var refCount1: UInt32
    var refCount2: UInt32
}

//函数返回值结构体
//BoxType 是一个泛型,最终是由传入的Box决定的
struct FunctionData<BoxType>{
    var ptr: UnsafeRawPointer//内嵌函数地址
    var captureValue: UnsafePointer<BoxType>
}

//捕获值的结构体
struct Box<T> {
    var refCounted: HeapObject
    //valueBox用于存储Box类型
    var valueBox: UnsafeRawPointer
    var value1: T
    var value2: T

}

//封装闭包的结构体,目的是为了使返回值不受影响
struct VoidIntFun {
    var f: () ->Int
}
//下面代码的打印结果是什么?
func makeIncrementer(forIncrement amount: Int, amount2: Int) -> () -> Int{
    var runningTotal = 1
    //内嵌函数,也是一个闭包
    func incrementer() -> Int{
        runningTotal += amount
        runningTotal += amount2
        return runningTotal
    }
    return incrementer
}
var makeInc = makeIncrementer(forIncrement: 10, amount2: 2)
var f = VoidIntFun(f: makeInc)

let ptr = UnsafeMutablePointer<VoidIntFun>.allocate(capacity: 1)
//初始化的内存空间
ptr.initialize(to: f)
//将ptr重新绑定内存
let ctx = ptr.withMemoryRebound(to: FunctionData<Box<Int>>.self, capacity: 1) {
     $0.pointee
}
print(ctx.ptr)
print(ctx.captureValue.pointee.value1)
print(ctx.captureValue.pointee.value2)

<!--打印结果-->
10
2

从打印结果可以看出,正好是传入的两个参数值

总结

  • 1、捕获值原理:在堆上开辟内存空间,并将捕获的值放到这个内存空间里

  • 2、修改捕获值时:实质是修改堆空间的值

  • 3、闭包是一个引用类型(引用类型是地址传递),闭包的底层结构(是结构体:函数地址 + 捕获变量的地址 == 闭包)

  • 4、函数也是一个引用类型(本质是一个结构体,其中只保存了函数的地址),例如还是以makeIncrementer函数为例

func makeIncrementer(inc: Int) -> Int{
    var runningTotal = 1
    return runningTotal + inc
}

var makeInc = makeIncrementer

分析其IR代码,函数在传递过程中,传递的就是函数的地址

将仿写的FunctionData进行修改

struct FunctionData{
    var ptr: UnsafeRawPointer//内嵌函数地址
    var captureValue: UnsafePointer<BoxType>
}

然后改版后的结构仿写如下

//函数也是引用类型
struct FunctionData{
    //函数地址
    var ptr: UnsafeRawPointer
    var captureValue: UnsafeRawPointer?
}

//封装闭包的结构体,目的是为了使返回值不受影响
struct VoidIntFun {
    var f: (Int) ->Int
}

func makeIncrementer(inc: Int) -> Int{
    var runningTotal = 1
    return runningTotal + inc
}

var makeInc = makeIncrementer
var f = VoidIntFun(f: makeInc)

let ptr = UnsafeMutablePointer<VoidIntFun>.allocate(capacity: 1)
//初始化的内存空间
ptr.initialize(to: f)
//将ptr重新绑定内存
let ctx = ptr.withMemoryRebound(to: FunctionData.self, capacity: 1) {
     $0.pointee
}

print(ctx.ptr)
print(ctx.captureValue)

<!--打印结果-->
0x0000000100003370
nil

通过cat命令查看该地址,地址就是makeIncrementer函数的地址

总结

作为一个开发者,有一个学习的氛围跟一个交流圈子特别重要,这是一个我的iOS开发交流群:130 595 548,不管你是小白还是大牛都欢迎入驻 ,让我们一起进步,共同发展!(群内会免费提供一些群主收藏的免费学习书籍资料以及整理好的几百道面试题和答案文档!)

  • 一个闭包能够从上下文中捕获已经定义的常量/变量,即使其作用域不存在了,闭包仍然能够在其函数体内引用、修改

    • 1、每次修改捕获值:本质修改的是堆区中的value值

    • 2、每次重新执行当前函数,会重新创建新的内存空间

  • 捕获值原理:本质是在堆区开辟内存空间,并将捕获值存储到这个内存空间

  • 闭包是一个引用类型(本质是函数地址传递),底层结构为:闭包 = 函数地址 + 捕获变量的地址

  • 函数也是引用类型(本质是结构体,其中保存了函数的地址)

Swift之深入解析闭包Closures的使用和捕获变量的原理 一、Closures 简介 ① 什么是闭包? 闭包(Closures)是自包含的功能代码块,可以在代码中使用或者用来作为参数传值。Swift 中的闭包与 C 和 Objective-C 中的代码块(blocks)以及其他一些编程语言中的匿名函数比较相似。 闭包是一个捕获了全局上下文的常量或者变量的函数,通俗来讲,闭包可以是常量也可以是函数。全局函数和嵌套函数其实就是特殊的闭包。 闭包的形式有: 全局函数:有名字但不能捕获任何值; 嵌套函数:有名字,也能捕获封闭函数内的值; 闭包表达式: 阅读详情

相关推荐

ios-Swift使用闭包实现类似于BlocksKit添加点击事件功能.zip

一行代码为UIView及其子类添加点击事件响应。 详情请看:https://github.com/britzlieg/ClosuresKit

swift 关于闭包值捕获问题

闭包在其定义的上下文变量中捕获常量和变量,即使这些常量和变量的原域已经不存在了,闭包仍然可以在闭包函数体内引用和修改这些值。问题探究,到底原域消失之后,这些变量是复制了一份在闭包引用内,还是之前变量或者常量都复用 func test(){ let myincr = increatmFunStr(Other: "BOB") print(myincr()) print(myincr()) print(myincr()) print(myincr(

书弋江山的博客 625

iOS中block变量捕获原理详析

Block概述 Block它是C语言级别和运行时方面的一个特征。Block封装了一段代码逻辑,也用{}括起,和标准C语言中的函数/函数指针很相似,此外就是blokc能够对定义环境中的变量可以引用到。这一点和其它各种语言中所说的&ldquo;闭包&rdquo;是非常类似的概念。在iOS中,block有很多应用场景,比如对代码封装作为参数传递。这在使用dispatch并发(Operation中也有BlockOperation)和completion异步回调等处都广泛应用。 Block是苹果官方特别推荐使用的数据类型,使用场景比较广泛 动画 多线程 集合遍历 网络请求回调 Block的作用 用来

iOS开发之Swift篇(7)—— 闭包

目录版本闭包的定义及使用闭包作为函数参数尾随闭包逃逸闭包 版本 Xcode 11.0 Swift 5.1 闭包的定义及使用 1. 概念 闭包是自包含的函数代码块,可以在代码中被传递和使用。Swift 中的闭包与 C 和 Objective-C 中的代码块(blocks)以及其他一些编程语言中的匿名函数(Lambdas)比较相似。 其实全局函数和嵌套函数实际上也是特殊的闭包,区别如下: 全局函数是...

看影成痴 466

【论文精读】Latent Diffusion

Latent Diffusion

weixin_44934783的博客 4476

华三模拟器实战:3分钟搞定路由器DHCP地址池配置(附常见错误排查)

本文提供华三模拟器中路由器DHCP地址池的实战配置指南。通过分步详解,帮助用户在3分钟内完成基础设置,并深入讲解多地址池、中继配置等进阶应用。文章重点剖析了DHCP服务无法工作的常见原因,提供从客户端到服务器的系统性排错方法,助力快速定位并解决地址分配失败、获取参数错误等典型问题。

w1x2y3的博客 360

IOS-闭包学习-Swift

闭包是自包含的函数代码块,可以在代码中被传递和使用。Swift 中的闭包与 C 和 Objective-C 中的代码块(blocks)以及其他一些编程语言中的匿名函数(Lambdas)比较相似。闭包可以捕获和存储其所在上下文中任意常量和变量的引用。被称为包裹常量和变量。Swift 会为你管理在捕获过程中涉及到的所有内存操作。

butterfly_new的博客 1835

深入浅出SSD:固态存储核心技术、原理与实战(文末赠书)

作为经典畅销书的升级版,本书在第1版的基础上新增了大量新知识(如闪存、UFS、测试、文件系统等),并改写或删减了许多过时或不适用的内容,总修改量超过60%。本书由SSD技术社区SSDFans的核心团队成员联合业界、高校SSD方面的专家共同完成,相对于第1版,内容更全面、更新,也更有深度。本书从产品、技术和应用3个维度展开,全面、详细讲解了SSD的发展历史、产品形态、工作原理、整体架构,以及各个功能模块的原理和使用,从SSD的前端接口协议到FTL算法,再到后端的闪存和纠错,以及测试,本书都有涉及。

阿玥的小博客 3124

【iOS开发--Swift语法】闭包学习记录

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

Chailo的博客 1158

东方财富 vs 新浪 vs AKShare:3 种股票实时数据接口性能与稳定性实测对比

本文深度评测了东方财富、新浪和AKShare三种股票实时数据接口的性能与稳定性。通过实际测试数据,从延迟、成功率和数据完整性三个维度进行全面对比,为量化交易和数据分析开发者提供选型参考。结果显示,东方财富接口在实时性和稳定性方面表现最优,适合高频交易场景。

weixin_30800807的博客 388

【全新官方接口】php检测微信域名拦截,检测微信域名被封

你的域名在微信被拦截的3种常见情况 1、域名因违规或有风险被拦截(红色拦截) 2、类似taobao.com,douyin.com这种的恶意竞争式拦截,淘宝、抖音等微信的竞争对手一般都是白色拦截(白色拦截) 3、有一个中间页的拦截,需要多次点击按钮才可以跳转到你的域名的,多是一些新型域名后缀,例如.top .xyz .link后缀,这种情况,备案后可以解决(备案拦截) 通过php get_headers()函数就可以分析到结果 这个下标为6是一个URL,在微信访问后正是这个讨厌的页面! 如果切换为

TANKING,开源开发者! 2876

iOS开发Swift-闭包

1.闭包表达式语法 { (参数) -&gt; return 类型 in //内容 } let names = ["C", "A", "E", "B", "D"] func back(_ s1: String, s2: String) -&gt; Bool { return s1 &gt; s2 //(B &gt; A, C &gt; B) }//闭包后: var rev...

LYly_B的博客 1770

计算机补码两位乘法移位补0还是1,补码两位乘.doc

补码两位乘沈阳航空航天大学课 程 设 计 报 告课程设计名称:计算机组成原理课程设计课程设计题目:COP2000实现补码两位乘法院(系):计算机学院专 业:计算机科学与技术(嵌入式系统方向)班 级学 号:2009040101181姓 名:张驰指导教师:杨华完成日期:2012年1月14日目 录第1章 总体设计方案11.1设计原理11.2设计思路11.3设计环境2第2章...

weixin_34471838的博客 967

iOS开发中Mockingjay与Alamofire集成指南:实现API模拟与独立测试

在软件开发中,API模拟(API Mock)是一种关键技术,它通过在客户端拦截网络请求并返回预设的模拟数据,来构建一个稳定可控的虚拟服务器环境。其核心原理是基于系统的URLProtocol机制,实现对URL Loading System中请求的透明拦截与响应构造。这项技术的核心价值在于赋能并行开发、保障测试可靠性以及模拟各类异常场景,从而显著提升开发与测试效率。在iOS开发领域,结合行业标准网络库Alamofire,轻量级框架Mockingjay为开发者提供了无缝的Mocking解决方案。通过匹配器(Mat

360

Unity3D实现视频和模型融合效果

在Unity中,视频和3D模型融合通常涉及到将视频表面内嵌到3D场景中。视频不影响模型的展示, 这可以通过使用Shader和Texture来实现。大家好,我是心疼你的一切,不定时更新Unity开发技巧,觉得有用记得一键三连哦。下面就让我们进入正文吧!提示:以下是本篇文章正文内容,下面案例可供参考本次介绍的就是视频和模型融合的实现,以后有其他的需要添加的会继续添加不定时更新Unity开发技巧,觉得有用记得一键三连哦。么么哒。

心疼你的一切博客 5357

华为OD机试 - 开源项目热度榜单(Java 2025 C卷 100分)

H = Wwatch X#watch + Wstar X#star +Wfork X#fork +Wissue X#issue +Wmr X#mr H表示热度值,Wwatch、Wstar、Wfork、Wissue、Wmr分别表示5个统计维度的权重,#watch、#star、#fork、#issue、#mr分别表示5个统计维度的统计值。

学Java,找哪吒 1756

iOS 对Strong-Weak Dance的思考

在使用 Block 时,除了使用 __weak 修饰符避免循环引用外,还有一点经常容易忘记。苹果把它称为:“Strong-Weak Dance”。 问题来源 这是一种 强引用 --&gt; 弱引用 --&gt; 强引用 的变换过程。在弄明白为什么要如此大费周章之前,我们首先来看看一般的写法会有什么问题。 __weak MyViewController

H.A.N的博客 607

Statistics and Machine Learning Toolbox

Statistics and Machine Learning Toolbox(带书签),MATLAB统计学习工具

A2UI Basic Catalog 实现指南:面向渲染器与客户端开发者的组件渲染与函数实现规范

> 本文以仓库内 [Basic Catalog Implementation Guide (v0.9.1)](https://link.gitcode.com/i/478c11a4203df097d09d5e55a9e7779a) 为骨架,结合 [web_core](https://link.gitcode.com/i/41f5d5e0e4f9d07020505eb61ac0f779) 的源码实现

gitblog_00286的博客 176

IEM模型,地表粗糙度模拟

利用微波反演土壤水分时候,利用IEM模型可以实现对随机地表的模拟

jre9下载 jdk9

(Java SE Runtime Environment 9.0) 9.0u175 官方最新版 【x86|x64】

Swift语言学习(六)

原文链接:http://www.ioswift.org/   11.闭包   闭包是可以在代码中传递和使用的自包含的功能模块。Swift 的闭包是类似于 C 、 Objective - C 的 block 、其他编程语言的lambdas。 闭包可以捕获和存储上下文任何引用常量和变量,也就是关闭这些常量和变量,因此得名“闭包”。Swift 管理所有闭包的捕获和内存操作。   ...

usench_10000的博客 227

复分析 阿尔福斯(Ahlfors) 第三版答案 英文

答案分为十章,有部分习题没有答案,全英文复分析阿尔福斯(Ahlfors) 第三版答案

iOS开发笔记之八十九——Swift中的闭包

*****阅读完此文,大概需要5分钟****** 一、闭包的基本概念与写法 1、Swift中闭包需要对比OC的block: OC的基本定义与写法: 返回值(^闭包名称)(参数类型 参数名) = ^(参数类型 参数名){函数体}; void(^XXBlock)(int a) = ^(int a){ NSLog(@"hello"); }; Swift基本定义与写法: {(参数:参数类型)-&gt;返回值类型 in 函数体}如果设置了返回值,记得返回对应类型的值 let test =..

iOS开发笔记 1万+

iOS-swift闭包(教学)

一. 闭包表达式(Closure Expression) 在Swift中,可以通过func定义一个函数,也可以通过闭包表达式定义一个函数,闭包表达式和闭包是两回事 闭包表达式的格式如下: { (参数列表) -&gt; 返回值类型 in 函数体代码 } 通过func定义一个函数: func sum(_ v1: Int, _ v2: Int) -&gt; Int { v1 + v2 } 通过闭包表达式定义一个函数: //通过闭包表达式定义一个函数,然后调用 var fn = { (v1: Int, v2: In

AAjjx的博客 464

iOS 闭包和Block的区别

本文对闭包和Block的概念、语法、内存管理和兼容性进行了详细的介绍。闭包是Swift语言的一部分,使用起来更加简洁和方便,而Block是Objective-C的特性,在Objective-C和Swift代码中都可以使用。同时,闭包和Block在语法、内存管理和兼容性上都存在一些区别,开发者在使用时需要根据具体的场景进行选择。通过对闭包和Block的比较和示例代码的说明,相信读者已经对它们有了更深入的理解,可以根据实际需求选择合适的方式来使用。

多分享一点点,共同进步一点点 1962

IOS 基础知识--闭包

IOS 基础知识--闭包1. 闭包简介2. 闭包语法3. 闭包用法3.1 Block声明及定义语法,及其变形3.2 typedef简化Block的声明3.3 Block的常见用法3.4 Block其他用法3.5 Block应用4. 闭包循环引用问题4.1 什么是循环引用4.2 常见的几种循环引用 1. 闭包简介 什么是闭包 闭包就是能够读取其他函数内部变量的函数,可以理解成“定义在一个函数内...

kyl282889543的博客 1068

Swift那些事儿 闭包(捕获值)

参考:http://www.runoob.com/swift/swift-closures.html 闭包可以在其定义的上下文中捕获常量或变量。 即使定义这些常量和变量的原域已经不存在,闭包仍然可以在闭包函数体内引用和修改这些值。 Swift最简单的闭包形式是嵌套函数,也就是定义在其他函数的函数体内的函数。 嵌套函数可以捕获其外部函数所有的参数以及定义的常量和变量。 import...

我的博客 1899

二十一 Swift5.0之 闭包捕获值

func getIncFunc(inc: Int) -> (Int) -> Int { var max = 10 func incFunc(x :Int) ->Int{ print("incFunc函数结束") // return inc + x max++ return max + x } // 当执行到

iOS鸽子的技术博客 924

Swift探索8:闭包的使用&捕获原理

本文主要分析闭包以及闭包捕获变量的原理 闭包 闭包是一个捕获了全局上下文的常量或者变量的函数,通俗来讲,闭包可以是常量也可以是函数 【全局函数是一种特殊的闭包】:定义一个全局函数,只是当前的全局函数并不捕获值 func test(){ print("test") } 【函数闭包】:下面的函数是一个闭包,函数中的incrementer是一个内嵌函数,可以从makeIncrementer中捕获变量runningTotal func makeIncrementer() -&g

changcongcong_ios的博客 1302
上一篇: iOS开发-Swift进阶之枚举enum!
下一篇: iOS开发-Swift进阶之闭包,逃逸闭包 & 非逃逸闭包!
iOS大鑫
博客等级 码龄6年 69粉丝 87原创
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值