这是我参与8月更文挑战的第11天,活动详情查看:8月更文挑战
1. block类型
block
是日常使用最多的代码块,面试中也会经常面到。block是对象
,它封装了一段代码,这段代码可以在任何时候执行。block可以作为函数参数
或者函数的返回值
,而其本身又可以带输入参数
或返回值
。与代理协议的功能一样,也可以用于事件通信
。Block分为3类
1.1 Global Block
全局block,位于全局区,block内部不使用外部变量,或者只使用静态变量和全局变量。
不使用外部变量:
使用内部变量:
使用静态变量:
1.2 MallocBlock
堆区Block,位于堆区,block内部使用局部变量或者oc属性,并且赋值给强引用或者copy修饰的变量。
使用局部变量:
使用属性:
1.3 StackBlock
栈区Block,位于栈区,和堆区block类似,block内部使用局部变量或者oc属性,但是不能赋值给强引用或者copy修饰的变量。
1.4 block的理解
我们对block有了区分,全局block比较好理解,但是堆区block 和栈区block比较容易混淆。我们来看下:
运行崩溃,我们自己实现了自定义Block,并把它的回调置为nil。
__weak
修饰的block内部使用了局部变量因此是栈区blcok
,强转称我们自定义blc,block本身是一个对象,因此复制了指针。- 之后strongBlock->strongBlock1
虽然 strongBlock1
拷贝一份到堆区
但是此时回调invoke
已经nil
了。
我们 weakBlock copy
之后相当于深拷贝,在对之前的weakBlock操作不影响了。
- 引用计数的影响
1.strongBlock 内部访问了不是静态变量,也没有弱引用修饰则是堆区Block,因此objc持持有
+1,copy
+1。
2. weakBlock是 弱引用修饰因此是栈区block
,只是持有
objc +1.
3. mallocBlock 把栈区block
进行copy因此是堆区block
,但是weakBlock 已经处理了外部的引用对象, mallocBlock不再对objc进行处理,只是把栈区block 拷贝到堆区,objc+1.
我们把mallocBlock = [strongBlock copy]
,并不会增加引用计数,因为他们都在堆区,只是拷贝了指针,从这也可以看出block是一个对象
。
继续看这个面试题:是否循环引用?
循环引用:staticSelf_ -> weakSelf -> self->staticSelf_
2. Block循环引用
self->block->self
,导致循环持有,无法释放导致内存泄露。通常系统会提示的。
不构成循环持有是不会造成循环引用的。
正常释放
:是指A持有B的引用,当A调用dealloc方法,给B发送release信号,B收到release信号,如果此时B的retainCount(即引用计数)为0时,则调用B的dealloc方法
循环引用
:A、B相互持有,所以导致A无法调用dealloc方法给B发送release信号,而B也无法接收到release信号。所以A、B此时都无法释放
解决循环引用我们要打破闭环即可,有以下几种方式。
2.1 weak-strong-dance
- 我们常用的
__weak
修饰,不会使self的引用计数发生变化。
- 套用block
正常情况调用2秒后没有问题,但是如果我们还没到2秒就返回,这个时候执行self已经销毁了,无法正常执行。
这时候我们需要对self进行强引用
其中strongSelf
是一个临时变量,在testBlock的作用域内,即内部block执行完
就释放strongSelf
2.2 __block修饰变量
通过__block
修饰对象,主要是因为__block修饰的对象是可以改变的,使用完成手动置为nil。
但是需要调用这个block,不然如果不调用block
,vc就不会置空,那么依旧是循环引用,self和block都不会被释放
2.3 持有对象作为参数
将对象self作为参数
,提供给block内部使用,不会有引用计数问题.
3. 总结
block主要分为3种类型,全局block
,堆区block
,栈区block
,block即是代码块
,也是一个对象
,本质是一个结构体
。可以作为参数
,返回值
,属性
。block的使用使我们的逻辑更加紧凑,传值更加方便。使用过程中注意循环引用
问题,导致内存泄露。打破循环主要有weak-strong-dance
(强弱共舞),__block修饰变量,手动置空
;持有对象作为参数
等。下一篇讲进行block的原理探讨。