一、Block定义
Block是什么?Block是一个带自动变量的匿名函数,大家知道C语言中没有匿名函数,所以可以说Block是C语言的的一种拓展。从OC的角度来看,Block是一个OC 对象,它内部也有isa指针。
那么Block的实质是什么?这是一段简单的Block定义:
int main(int argc,char* argv[]) {
@autoreleasepool {
int a=10;
void(^BlockName)() = ^() {
NSLog(@"block内部a-%d",a);
};
BlockName();
}
}
然后用命令行转化成C++ 看其内部实现
clang -rewrite-objc main.m
我们可以看到Block中调用了__main_block_impl_0函数,并将这个函数的地址赋值给了Block,我们看下__main_block_impl_0这个结构体的具体实现:
我们可以看到这个结构体内部还有一个同名构造函数__main_block_impl_0的,构造函数传递了 四个参数:
第一个参数__main_block_func_0 : 取出对应a参数 copy,NSLog...,这个函数对应存储了原来Block中的代码;
第二个参数__main_block_desc_0_DATA: 其内包含两个参数 reserved、Block_size,并且reserved赋值为0而Block_size则存储着__main_block_impl_0的占用空间大小,传递对应的地址入参;
第三个参数a:就是我们定义的局部变量。
所以,这边也可以理解了,当局部变量传入Block之后,再次修改局部变量的值之后是无法被Block捕获的。因为局部变量参数是作为值传入Block内部的存储在__main_block_impl_0结构体内部的,因此修改局部变量值之后Block输出还是之前存储的那个值。
可以通过下图看下Block内各个函数之间的关系:
__Block修饰后可以修改局部变量???
我们来看下__Block修饰后的c++实现:
我们可以看到原来 __block int a=10;会被处理成 __Block_byref_a_0 a=...再看下这个结构体实现:
可以看到结构体内有一个forwarding指针和一个与原变量相同类型的成员变量,forwarding指针指向结构体内的成员变量。在__Block修饰的变量,会被实现为__Block_byref_a_0类型,在block内外,都通过forwarding来访问的。这是与之前的值类型传递不一样的地方,也是能修改局部变量的原因。
二、Block使用场景
这是Block最完整的定义:
/* returnType:返回类型
blockName:函数名称
parameterTypes:参数类型
parameters: 参数名称
*/
<#returnType#>(^<#blockName#>)(<#parameterTypes#>) = ^(<#parameters#>) {
<#statements#>
};
Block因其方便易读的特点在我们日常开发中使用较为频繁:
1、typedef Blcok属性,通过Block实现事件的响应或者数据的传递
typedef void(^Block) (int a,int b);
@property (nonatomic,copy) Block *myBlock;
2、Block作为方法参数,利用Block实现回调
[UIView animateWithDuration:<#(NSTimeInterval)#> animations:<#^(void)animations#> completion:<#^(BOOL finished)completion#>]
3、利用Block返回自身self对象等实现链式语法
MASonry使用
三、Block内存管理
Block主要有三种类型
__NSGlobalBlock__ ( _NSConcreteGlobalBlock )
__NSStackBlock__ ( _NSConcreteStackBlock )
__NSMallocBlock__ ( _NSConcreteMallocBlock )
看下不同类型的Block的内存分配
那么这几种类型的Block都是如何定义的呢
__NSGlobalBlock__:没有访问auto对象,存放在数据段中
__NSStackBlock__:访问了auto对象,存放在栈中;
__NSMallocBlock__:__NSStackBlock__调用copy成为__NSMallocBlock__类型,并保存在堆中,由程序员负责管理。而这些很多情况下在都由ARC帮我们处理。
四、Block循环引用
在Block内使用self,一般会导致循环引用,这是为什么呢?
循环引用是什么意思呢?简单来说就是堆上的对象与堆上的对象互相引用造成的环Block作为self的属性,self对Block是强引用,而在Block内部引用self某个属性,实现了Block对self的强引用,循环引用就这样产生了。
为了避免循环引用,同时为了防止异步的block在回调的时,block执行的过程中被意外释放,我们可以先定义一个weak类型的对象供Block内部使用,再用__strong 对block外的__weak对象再次强引用,既能够防止引用循环,又能够保证代码的正确执行。
__weaktypeof(self) weakSelf =self;
_myBlock= ^(inta ,intb){
__strongtypeof(self) strongSelf = weakSelf;
strongSelf.age =10;
};
参考
iOS底层原理总结
网友评论