iOS底层之Block

一、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底层原理总结

https://juejin.im/post/5b0181e15188254270643e88#heading-9

你可能感兴趣的:(iOS底层之Block)