底层原理-26-Block(上)

这是我参与8月更文挑战的第11天,活动详情查看:8月更文挑战

1. block类型

block是日常使用最多的代码块,面试中也会经常面到。block是对象,它封装了一段代码,这段代码可以在任何时候执行。block可以作为函数参数或者函数的返回值,而其本身又可以带输入参数返回值。与代理协议的功能一样,也可以用于事件通信。Block分为3类

1.1 Global Block

全局block,位于全局区,block内部不使用外部变量,或者只使用静态变量和全局变量。
不使用外部变量:
image.png
使用内部变量:
image.png
使用静态变量:
image.png

1.2 MallocBlock

堆区Block,位于堆区,block内部使用局部变量或者oc属性,并且赋值给强引用或者copy修饰的变量。
使用局部变量:
image.png
使用属性:

image.png

1.3 StackBlock

栈区Block,位于栈区,和堆区block类似,block内部使用局部变量或者oc属性,但是不能赋值给强引用或者copy修饰的变量。
image.png

1.4 block的理解

我们对block有了区分,全局block比较好理解,但是堆区block 和栈区block比较容易混淆。我们来看下:

image.png
运行崩溃,我们自己实现了自定义Block,并把它的回调置为nil。

  1. __weak修饰的block内部使用了局部变量因此是栈区blcok,强转称我们自定义blc,block本身是一个对象,因此复制了指针。
  2. 之后strongBlock->strongBlock1

image.png
虽然 strongBlock1 拷贝一份到堆区但是此时回调invoke已经nil了。

image.png
我们 weakBlock copy 之后相当于深拷贝,在对之前的weakBlock操作不影响了。

  • 引用计数的影响

image.png
1.strongBlock 内部访问了不是静态变量,也没有弱引用修饰则是堆区Block,因此objc持持有+1,copy+1。
2. weakBlock是 弱引用修饰因此是栈区block,只是持有objc +1.
3. mallocBlock 把栈区block 进行copy因此是堆区block,但是weakBlock 已经处理了外部的引用对象, mallocBlock不再对objc进行处理,只是把栈区block 拷贝到堆区,objc+1.

image.png
我们把mallocBlock = [strongBlock copy],并不会增加引用计数,因为他们都在堆区,只是拷贝了指针,从这也可以看出block是一个对象

image.png
继续看这个面试题:是否循环引用?

image.png
循环引用:staticSelf_ -> weakSelf -> self->staticSelf_

2. Block循环引用

image.png
self->block->self,导致循环持有,无法释放导致内存泄露。通常系统会提示的。

image.png
不构成循环持有是不会造成循环引用的。

  • 正常释放:是指A持有B的引用,当A调用dealloc方法,给B发送release信号,B收到release信号,如果此时B的retainCount(即引用计数)为0时,则调用B的dealloc方法

image.png

  • 循环引用:A、B相互持有,所以导致A无法调用dealloc方法给B发送release信号,而B也无法接收到release信号。所以A、B此时都无法释放

image.png
解决循环引用我们要打破闭环即可,有以下几种方式。

2.1 weak-strong-dance

  • 我们常用的__weak 修饰,不会使self的引用计数发生变化。

image.png

  • 套用block

image.png
正常情况调用2秒后没有问题,但是如果我们还没到2秒就返回,这个时候执行self已经销毁了,无法正常执行。
image.png
这时候我们需要对self进行强引用

image.png
其中strongSelf是一个临时变量,在testBlock的作用域内,即内部block执行完就释放strongSelf

2.2 __block修饰变量

通过__block修饰对象,主要是因为__block修饰的对象是可以改变的,使用完成手动置为nil。
image.png
但是需要调用这个block,不然如果不调用block,vc就不会置空,那么依旧是循环引用,self和block都不会被释放

2.3 持有对象作为参数

image.png
将对象self作为参数,提供给block内部使用,不会有引用计数问题.

3. 总结

block主要分为3种类型,全局block堆区block栈区block,block即是代码块,也是一个对象,本质是一个结构体。可以作为参数返回值属性。block的使用使我们的逻辑更加紧凑,传值更加方便。使用过程中注意循环引用问题,导致内存泄露。打破循环主要有weak-strong-dance(强弱共舞),__block修饰变量,手动置空持有对象作为参数等。下一篇讲进行block的原理探讨。

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享