Block深入学习


Block 带有自动变量和相关匿名函数的对象。

为什么出现Block,和block相比的就是函数,自带了执行上下文。函数内部内部,只能使用传入参数和全局变量。而Block的出现,对于Block内部使用的局部变量和对象(普通局部变量,复制值,使用__block 修饰,封装保存__block变量,局部静态变量,和对象需要保存指针 ,超出局部变量的作用域),编译器会自动将其封装到Block内部的结构体中,当Block使用的时候,就能使用这个超出了作用域的局部变量的值和对象。相当于编译器帮程序员实现了执行上下文封存。方便了操作。

注意:对于普通的对象方法,之所以能使用self这个关键字,来访问对象的成员变量和方法等,是因为对象方法编译成的函数,第一个参数就是当前对象。这和Block还是有区别的,Block能catch任意对象。先有runtime 后有Block

匿名函数:(没有名称的函数,只能通过函数指针进行调用)block 内部包含着一个匿名函数的函数指针,执行block就是执行这个匿名函数

自动变量:block内部会自动保存函数执行过程中需要的对象地址,或是值(保存自动变量的瞬间值)


Block 的格式,可以省略返回值,参数列表


可以将产生的BLock赋值给Block类型的变量,这个变量和基本的变量一致,可以作为局部变量,全局变量,函数参数,自动变量


截取自动变量值:在产生Block会截取局部变量的当前的瞬间值,之后就不会被局部变量的值变化影响,执行Block的时候还是会使用原来的值。Block内部使用的值是产生的时候的局部变量的瞬间值。

如果这里改变局部变量的值,会出现错误:

Variable is not assignable (missing __block type specifier)

只有使用__block 修饰之后,才能在block内部修改局部变量,在内部修改之后,局部变量的值也发生了修改


在block内部截获OC对象,并对OC对象进行赋值会发生错误。但是使用这个OC对象是没有问题,仍然能调用这个对象的方法。

Block 深入解析

编译后的代码:


源代码

分析编译产生的CPP文件:__block_impl 这个结构体也有isa指针,说明Block也是一个对象



对于变量的截获:

一、对于局部基础变量,会直接截获这个基础变量的值,但是不能在这个Block修改这个局部变量,不然出现编译错误,因为catch的就是这个栈区局部变量的值,并没有这个栈区变量的地址,不能修改这个基础变量了。


1、是值传递,不是地址传递

2、至于编译失败,可能是不想让外界使用的时候出现歧义,这里的值和外面栈的局部变量已经没有关系了。

二、使用全局变量,局部静态变量


对于全局变量,不用进行截获,直接使用就行。对于局部静态变量,还是要截获,保存在block结构体中。这样就能使用局部静态变量超过作用域使用

传入局部静态变量的指针作为参数:


使用:

三、使用__block 进行修饰的局部变量

__block 修饰局部变量,代表这个局部变量会保存到Block结构体内部:


转换后的代码:


__block变量的结构体


使用__block 变量


当Block截获这个变量的时候:


__forwarding 指向当前的__block 变量

多个Block,可以截获同一个__block 变量,一个block修改了这个变量,那么后一个block使用的时候,就是修改后的值


Block的存储栈 堆  全局(程序的数据区域.data)

1、全局Block:在全局区创建的Block变量,因为不用catch任何局部变量,只使用全局变量,所以可以直接放到.data 区域(对于一些不是在全局区声明的Block,但是没有catch变量的Block,也会是GlobalBlock的类型)


2、_NSConcreteStackBlock 一般产生的都是栈区block

栈区的Block,超出作用域的时候,block回收,而对应的__block 变量也会回收

3、mallocBlock,在进行copy操作的时候,会将栈区的Block,copy到堆区,改变Block的isa指针

对于ARC情况下,会自动的将Block 从栈区拷贝到堆区:objc_retainBlock(非ARC下面,还是要自己进行拷贝的)

早期下面的代码就会出现问题,因为没有将StackBlock 拷贝到堆区,一旦栈区回收,再去通过Array调用Block,就会出现问题

但是现在已经没有问题,通过测试,向NSArray中存放Block对象,Block对象会被自动copy,Array会对里面的对象强引用,

弱指针:

强指针:


对于作为参数,和返回值的Block,作为参数传递,不会进行copy,但是作为返回值,会进行copy,也会使用objc_autoreleaseReturnValue,进行参数内存管理。

当copy Block时候对于他引用的__block 变量的影响:当copy Block的时候,会同时将栈区的__block 变量拷贝到堆区,使用__Block_object_assign    

多个Block使用同一个__block变量,第一个block copy到堆区,__block 变量也进行复制了,第二个Block变量copy的时候,只是对__block 变量的引用计数+1            

__forwording 对于__block 变量,复制到堆区,那么其栈区的__block 变量的__forwoding 指向的不再是栈区的__block 地址了,而是堆区的,所以才能保证两个变量的值同步,修改一个,另外一个也修改



Block内部截获OC对象,在Block结构体内部会强引用这个对象,保证这个对象不会因为超过作用域,而被回收:


这个对象的回收也是会被Block影响,当Block变量回收的时候,就会调用_Block_object_dispose方法,参数是这个对象,这个对象的引用计数也会减一

Block内部使用weak指针的,Block也不会进行强持有,会存在可能Block使用时对象已经回收的问题:

循环引用的问题:使用__weak 修饰self,Block内部使用弱指针。为了避免使用过程中被回收,可以在内部转成强指针使用

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 一、tween 缓动--简单的Tween缓动--逐字缓动缓动--缓动函数演示缓动动画 1.可以看出,帧循环fram...
    合肥黑阅读 13,010评论 0 2
  • 不知道是从什么时候开始,以文字记录生活就成了我的日常,和吃饭睡觉一样,不可或缺。 一开始觉得养成写作的习惯真难,现...
    王子月阅读 2,920评论 0 1
  • 又是秋雨蟹肥时,思绪如泉水般涌来,都说人在某一阶段会突然爱上写作,我想便是此时吧,给自己一个机会和空间倒空自己,放...
    Samanthaguan阅读 1,176评论 0 0
  • 我记得有次我看到过这样的一句话——如果把爱情比作一幅画,那么普通女人的爱情则是水墨丹青,没有起伏,而文艺女青年的爱...
    柒咫阅读 4,318评论 0 3
  • 天黑 相思的人狼狈 放开 不舍又何必防备 我的以为 是可逆转车轮 你的心 像不见底的咖啡 晨曦 烟火被慢慢勾起 眼...
    林映澈阅读 975评论 0 1