一.闭包的内存
1.闭包捕捉的num
相当于 类的成员变量,闭包的方法,相当于类的实例方法。
typealias Fu = (Int) - > Int
func getFn() -> Fn {
//局部变量,这里会分配堆空间 便于下面的函数捕获他
var num = 0
func plus(_ v1 : Int) ->Int{
num += v1
return num
}
return plus
}
var fn1 = getFn()
- 查看
fn1
的内存地址、以及内容一般的常用方法在这里不支持
//打印普通变量地址,在这里含有函数,所以无法打印
print(Mens.ptr(ofVal: &fn1))
print(Mens.memStr(ofVal: &fn1))//打印普通对象内容
- 借助汇编语言来做
MemoryLayout.stride(ofValue: fn1)//占用多少字节
//16 字节
截屏2020-07-28 下午2.36.00.png这里注意内存地址与内存地址的存储内容的区别(类似于房间编号跟房间里的人的区别)
-
movq
:每次向后移动8个字节,把rax
赋值给后面的地址值 -
call
函数调用,后面接函数地址
结论:
-
fun1
总共占用16个字节的内存 -
fun1
的前面 8 个字节放置的就是plus的函数地址 -
fun1
的后面的8个字节放置的是堆空间的地址值(房间里面放的是num的数值) -
plus
接受了两参数:i
的值跟num的堆空间地址值
-
num
的释放时机:arc下 没有强指针指向它时候 就会被释放。没有内存泄漏的风险。 -
num
的捕获时机:在调用return
返回函数地址时捕获 - 如果
num
是全局变量,plus函数是不会捕获num
的值的,没有分配堆空间。
func getFn() -> Fn {
//局部变量,这里会分配堆空间 便于下面的函数捕获他
var num = 0
func plus(_ v1 : Int) ->Int{
num += v1
return num
}
num = 10
return plus
}
var fn1 = getFn()
print(fn1(1)) //打印结果是:11
注意:这里print(fn1(1)) //打印结果是:11。这里捕获的变量值是10,而不是0。捕获的时机是在
return返回函数的时候
。
练习1.
截屏2020-07-28 下午3.28.28.png这里:i << 1 相当于 i * 2
let (p,m) = getFuns()
print(p(6)) // (6,12)
print(m(5)) // (1,2)
print(p(3)) // (5,10)
print(m(2)) //(2,4)
- getFun 返回 两个函数组成的元组,
- 打印结果说明,这里 num1 跟num2 都是同一份,累计加减的结果。两个函数共享num1、mum2,捕获的变量相当于成员变量。
- 类似下面的结构:
- 元组这里面的元素能直接拿出来用,这里
p
=plus
,m
=minus
。这里num1 跟 num2 都只是分配了 一次堆空间。
练习2.
var funs : [() -> int] = []
for i in 1...3{
funs.append { i } // 尾随闭包的简写
/*
相当于:
func myFunc () -> Int {
return i
}
funs.append{ myFunc}
*/
}
for f in funs {
print(''\(f())'')//打印的是函数 // 打印数值:1、2、3
}
类比:
截屏2020-07-28 下午4.04.47.png
2. 注意:
截屏2020-07-28 下午4.08.38.png3. 自动闭包
截屏2020-07-28 下午4.17.31.png- 这里 v2:变成函数 的目的是,当第一个条件满足时候,不在运行后面的函数。
如果这样写
getFirstPositive(_ v1:Int , _ v2: Int) -> Int{ return v1 > 0 ? v1 : v2}
, 即使是v1 > 0, v2函数 仍然还是会调用。上面的写法是一种优化。
截屏2020-07-28 下午4.24.45.png
@autoclosure()
自动闭包:
- 会自动将 参数20 生成闭包表达式。如果不写
@autoclosure()
则写法为:getFirstPositive(10,{20})
-
@auticlosure () -> Int
: 只支持 无参数的,并且有返回类型的闭包、 -
其他说明:
截屏2020-07-28 下午6.34.13.png
- @autoclosure 可以构成函数的重载;
网友评论