Block

2018-08-28  本文已影响28人  蔚尼

一.block的本质

block本质是一个OC对象。封装了函数调用地址和函数调用环境(参数)


block本质

二.block的变量捕获(capture)

三.block的类型

1. block类型

block有3种类型,可以通过调用class方法或者isa指针查看具体类型,最终都是继承自NSBlock类型
NSGlobalBlock ( _NSConcreteGlobalBlock )
NSStackBlock ( _NSConcreteStackBlock )
NSMallocBlock ( _NSConcreteMallocBlock )

2. block内存分配

block内存分配

3. 不同类型的block

image.png

4.block的copy(block怎么从堆复制到栈上面)

在ARC环境下,编译器会根据情况自动将栈上的block复制到堆上,比如以下情况

注意,是对栈上的block进行以上操作才可以

block作为返回值.png 堆上的block进行强引用.png
堆上的block进行强引用.png

上面使用@property进行修饰,block中用了atuo变量,block在栈上,所以让栈上的block复制到堆上。
如果是未使用atuo变量,block是global类型的,无法复制到堆上。

复制到堆上的block

为什么mrc下建议用copy、arc下建议用copy、strong呢?
因为对于堆上的block。
mrc下把block进行copy会放到堆上。arc下把blcok进行强引用,会复制到堆上。
放到栈上的block出了方法会被释放掉,堆上的不会。所以这么做。

5.block与对象类型的auto变量

上面讲到,block使用auto变量就是栈类型的。那么block内部访问的auto变量是对象类型,block内部对这个对象是强引用还是弱引用呢?

当block内部访问了对象类型的auto变量时:

如果block是在栈上,将不会对auto变量产生强引用
(参考图【栈上面block实例】)

如果block被拷贝到堆上

  • 会调用block内部的copy函数
  • copy函数内部会调用_Block_object_assign函数
  • _Block_object_assign函数会根据auto变量的修饰符(__strong、?>__weak、__unsafe_unretained)做出相应的操作,形成强引用(retain)或者弱引用
    (参考【堆上的blcok强引用】、【堆上的blcok弱引用】)

如果block从堆上移除

  • 会调用block内部的dispose函数
  • dispose函数内部会调用_Block_object_dispose函数
  • _Block_object_dispose函数会自动释放引用的auto变量(release)

弱引用的对象,出了对象所在的自动释放池就释放了。
强引用的对象,和block一起销毁。block出了所在的block释放。具体看下面:

函数调用时机 栈上面block实例 堆上的blcok强引用
堆上的blcok弱引用

四.__block

  __block int age = 10;
    MyBlock block = ^(){
    
        age = 20;
        NSLog(@"%d",age);
    };
    
    block();
    __block Person * person = [[Person alloc] init];    
    
    MyBlock block1 = ^(){
        
        person = [[Person alloc] init];
    };  

__block内存管理

如下:
block0、block1在栈上的时候同时持有__block修饰的变量。
block0复制到堆上的时候,__block修饰的变量也复制到堆上、被强引用了。


image.png

block1复制到堆上的时候,因为持有的__block已经在堆上了,就继续指向原本的__blcok修饰的变量。


image.png image.png image.png

内存管理总结

变量类型 是否捕获到内部 访问方式
局部 auto修饰 值传递
局部 static修饰 指针传递
全局变量 直接访问
栈类型block 堆类型block
auto修饰的对象 弱引用 根据所指向对象的修饰符形成强引用(retain)或者弱引用
__block修饰的对象 弱引用 根据所指向对象的修饰符形成强引用(retain)或者弱引用
__block修饰的变量 弱引用 强引用

上面三者:auto修饰的对象、__block变量、__block修饰的对象:

当block拷贝到堆上时,都会通过copy函数来处理它们
当block从堆上移除时,都会通过dispose函数来释放它们

__block的__forwarding指针

上面有提到,__block修饰的变量会被包装成一个对象。
这个对象里面有__forwarding指针,是指向自身的_isa指针。


__block的_forwarding指针

当block从栈上复制到堆上,__block生成的对象也会复制到堆上。栈上的__block内部_forwarding指针会指向堆上的__block生成的对象。如下图:


image.png

五.循环引用

解决循环引用问题 - ARC

解决方法 解决原理

因为上面强引用了,所以直接采用弱引用,发现也解决循环引用的问题了:


__block解决方法2

解决循环引用问题 - MRC

六.面试问题

注意点:
__block会进行相应的内存管理;(比如堆上的block,会根据__block对象的修饰符进行强引用或者弱引用)

mrc环境下,不会对__block修饰的变量进行强引用;

上一篇 下一篇

猜你喜欢

热点阅读