(本人的文章只说个人理解,如有错漏,不吝指正,感谢)
定义:带有自动变量的匿名函数。
这里我有两个问题,自动变量和匿名函数。
我们先研究他的函数再研究他的变量,所以先说匿名函数:
block其实就是普通的C函数,C函数是支持以指针形式访问的,但是C函数不支持方法不命名。
C函数的指针访问如图,Block的匿名实现其实就是隐去了声明(int func(int num))与指针赋值(int(*funcMB)(int) =&func;)的过程,内部必然是同样需要函数名的,OC基于C。
Block隐去声明之后只能指针访问,那他同样需要声明指针,所以会有一步(typedef void(^func_Mirror)(int num);)的过程,他并不是声明指针,他是声明指针格式,等价于(int (* funcMB)(int))。如此就可以通过OC提供的匿名创建方法创建C方法并得到指定格式的指针。
我们来梳理一下Block的流程:
1.先声明匿名函数的指针类型,typedefvoid(^func_Mirror)(intnum);(此时并不具备函数实体,就像OC方法的声明与实现)
2.赋值并返回指针,_block=^(int num){};(此时初始化C函数并传入上下文)
3.通过指针传入参数并执行此C函数,_block(10);(这里的上下文与参数并不是相同的概念,上下文关系到函数的实现,在函数初始化时即确定上下文实际值,而函数初始化与实参是没有任何关系的,实参只需要在函数执行时传入)
测试代码 结果由此,i在block初始化时被copy到C函数体中,与后续的i再无关系,而参数则会在每次执行block时都读取;
到这里,关于匿名的部分我的理解就说完了。
__block
1.普通的上下文变量无法被重新赋值,否则会编译不通过,这是因为上下文在block初始化的时候就需要确定值,使用OC的对象类型可以在block内部调用方法改变其值,block引用OC对象本质上是使用其指针,OC对象调用方法不会改变自身指针而是改变自身结构中的值,所以也就没有修改block的上下文,如果在block内部试图重新赋值OC指针,编译报错;
2.__block会让原类型变成__Block_byref_val类型,原值传入此类型作为属性,此类型变量成为block成员属性,block将通过copy与dispose管理其内存。(__Block_byref_val 是系统固有类型,非动态生成,只是动态生成此类对象)
内存
Block内部存在isa指针,本质上是OC对象的一种,它的class分为三种:_NSConcreteStackBlock,_NSConcreteGlobalBlock,_NSConcreteMallocBlock;
实现在方法内的是_NSConcreteStackBlock,实现在方法外的是_NSConcreteGlobalBlock,_NSConcreteMallocBlock由_NSConcreteStackBlock copy生成。
_NSConcreteStackBlock:写在方法里,存在栈上,使用时copy到堆,内部变量的 __forwarding(__block类型变量的self指针)指向堆,出方法废弃;
_NSConcreteMallocBlock:不显式声明,存在堆里,由栈block copy而来,copy时copy所有变量并引用,被其他对象引用,服从OC内存管理;
_NSConcreteGlobalBlock:写在方法外,存在.data区,无法截获自动变量,代码执行时声明,代码结束时释放,不需要内存管理。
copy
栈block使用时会自动调用copy方法复制到堆;例如:调用,返回,手动调用copy,retain,赋值等等;
例外情况:block作为参数传给方法时(参数被usingBlock修饰或GCD方法除外)
手动调起copy时:
堆block :引用计数+1; 栈block :copy到堆; 全局block :什么都不做;
__autoreleasing 与__block冲突;
循环引用(针对OC对象)
block内变量(或变量的方法与属性)与block互相持有会引起循环引用。
解决:__weak可以使block不持有对应变量,但变量可能提前释放,block不安全;
不用修饰词,相当于__strong,只要在block执行完毕之后将其置nil 就可以打破block对其的引用,但变量不安全,block外部,此变量也被置nil,其次有的变量(self)无法被置nil;
__block,__block会使block引用相对应的block属性,block属性与对应属性与block依然是循环引用,但可以通过给block属性赋值nil打破此循环,手动赋值nil保证变量不被提前释放,缺点,只有block属性被置nil时循环引用才被打破;
MRC/ARC
以上均是默认ARC环境下的结论,MRC下,__block使block不会retain(强引用)对应变量,主要用于避免循环引用。
网友评论