发布网友 发布时间:2022-10-12 16:44
共1个回答
热心网友 时间:2023-10-23 04:19
Block 是将函数及其执行上下文封装起来的对象。 比如:
通过 clang -rewrite-objc WYTest.m 命令编译该 .m 文件,发现该 block 被编译成这个形式:
其中 WYTest 是文件名, blockTest 是方法名,这些可以忽略。其中 WYTest blockTest_block_impl_0 结构体为
--block_impl 结构体为
block 内部有 isa 指针,所以说其本质也是 OC 对象
block 内部则为:
所以说 Block 是将函数及其执行上下文封装起来的对象
既然 block 内部封装了函数,那么它同样也有参数和返回值。
如果你正在跳槽或者正准备跳槽不妨动动小手,添加一下咱们的交流群1012951431来获取一份详细的大厂面试资料为你的跳槽多添一份保障。
这里的输出是 6 而不是 2,原因就是对局部变量 num 的截获是值截获。同样,在 block 里如果修改变量 num ,也是无效的,甚至编译器会报错。
打印为 1,2,3
局部对象变量也是一样,截获的是值,而不是指针,在外部将其置为 nil ,对 block 没有影响,而该对象调用方法会影响
输出为 2,意味着 num = 1 这里的修改 num 值是有效的,即是指针截获。同样,在 block 里去修改变量 m ,也是有效的。
编译后
( impl.isa = &_NSConcreteStackBlock ;这里注意到这一句,即说明该 block 是栈 block )
可以看到局部变量被编译成值形式,而静态变量被编成指针形式,全局变量并未截获。而 --block 修饰的变量也是以指针形式截获的,并且生成了一个新的结构体对象:
该对象有个属性: num5 ,即我们用 --block 修饰的变量。这里 --forwarding 是指向自身的(栈 block )。
一般情况下,如果我们要对 block 截获的局部变量进行赋值操作需添加 --block 修饰符,而对全局变量,静态变量是不需要添加 --block 修饰符的。
另外, block 里访问 self 或成员变量都会去截获 self 。
分为全局 Block(_NSConcreteGlobalBlock) 、栈 Block(_NSConcreteStackBlock) 、堆
Block(_NSConcreteMallocBlock) 三种形式
其中栈 Block 存储在栈 (stack) 区,堆 Block 存储在堆 (heap) 区,全局 Block 存储在已初始化数据 (.data) 区
输出:
比如:
输出:
日常开发常用于这种情况:
比如堆 1中的全局进行 copy 操作,即赋值:
输出:
仍是全局 block
而对 2中的栈 block 进行赋值操作:
输出:
对栈 blockcopy 之后,并不代表着栈 block 就消失了,左边的 mallock 是堆 block ,右边被 copy 的仍是栈 block 比如:
输出:
即如果对栈 Block 进行 cop ,将会 copy 到堆区,对堆 Block 进行 copy ,将会增加引用计数,对全局 Block 进行 copy ,因为是已经初始化的,所以什么也不做。
另外, --block 变量在 copy 时,由于 --forwarding 的存在,栈上的 --forwarding 指针会指向堆上的-- forwarding 变量,而堆上的 --forwarding 指针指向其自身,所以,如果对 --block 的修改,实际上是在修改堆上的 --block 变量。
即 --forwarding 指针存在的意义就是,无论在任何内存位置,都可以顺利地访问同一个 --block 变量。
另外由于 block 捕获的 --block 修饰的变量会去持有变量,那么如果用 --block 修饰 self ,且 self 持有
block ,并且 block 内部使用到 --block 修饰的 self 时,就会造成多循环引用,即 self 持有 block , block 持有 --block 变量,而 --block 变量持有 self ,造成内存泄漏。
比如:
如果要解决这种循环引用,可以主动断开 --block 变量对 self 的持有,即在 block 内部使用完 weakself 后, 将其置为 nil ,但这种方式有个问题,如果 block 一直不被调用,那么循环引用将一直存在。
所以,我们最好还是用 --weak 来修饰 self
以上就是 block 篇的面试题合集了,感谢观看~!