美文网首页
Objective-C 语言特性之block

Objective-C 语言特性之block

作者: 锋尚达人 | 来源:发表于2021-03-27 10:47 被阅读0次

一:blcok本质

Block本质上也是一个oc对象,它内部也有一个isa指针。

Block 时是装了函数调用以及函数调用环境的oc对象。

二:block 结构:

struct__blockTest_block_impl_0{

    struct__block_impl  impl;

    struct__blockTest_block_desc_0* Desc;

    __blockTest_block_impl_0   (void*fp,struct__blockTest_block_desc_0*desc,intflags=0){

            impl.isa=&_NSConcreteStackBlock;

            impl.Flags=flags;

            impl.FuncPtr=fp;Desc=desc;

    }

};

__blockTest_block_impl_0是Block的C++实现,是一个结构体,从命名可以看出表示blockTest中的第一个(0)Block。通常包含两个成员变量__block_impl impl,__blockTest_block_desc_0* Desc和一个构造函数。

struct__block_impl{

        void *isa;

        int    Flags;

        intReserved;

        void*FuncPtr;

};

__block_impl也是一个结构体

    (1)*isa:isa指针,指向一个类对象,有三种类型:_NSConcreteStackBlock、_NSConcreteGlobalBlock、_NSConcreteMallocBlock,本例中是_NSConcreteStackBlock类型。

    (2)Flags:block 的负载信息(引用计数和类型信息),按位存储。

    (3)Reserved:保留变量。

    (4)*FuncPtr:一个指针,指向Block执行时调用的函数,也就是Block需要执行的代码块。在本例中是__blockTest_block_func_0函数。

三:block 捕获机制

为了保证block内部能够正常访问外部的变量,blcok 有个变量捕获机制。

(1)自动变量

        针对局部变量中的自动变量,会通过值传递的方式捕获到block 中,由于是值传递捕获的变量,所以在blcok内部无法感知外部的对变量的修改,并且会在block变量中复制一份该变量。  Block 之所以会通过值传递的方式保存这种变量,也是因为这种变量是分配在栈中的,一旦变量所在的函数调用完毕,该变量就会被其他内容覆盖,但是block 有可能还需要使用该变量,所以block需要捕获,并保存一份副本。

(2)静态变量

        针对局部变量中的 static变量,由于static的生命周期是整个文件,所以并不存在自动变量的情况,不需要复制副本,所以通过指针传递的方式捕获该变量到block内部。由于是指针传递方式捕获,所以外部对该变量的修改,block内部可感知,并且能正常使用该变量。

(3)针对全局变量。

        Block 不会捕捉全局变量,而是通过内存地址直接使用该变量。 因为全局变量的生命周期更长,不会出现被覆盖的情况。

block捕获变量总结

(4)对象类型的捕获

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

            如果block 是在栈上,将不会对auto变量产生强引用

        <2>如果block被拷贝到堆上

            会调用block内部的copy函数。Copy函数内部会调用_Block_object_assign函数。

            _Block_object_assign函数会根据auto变量的修饰符(__strong,__weak,__unsafe_unretain)作出对应的操作,形成强引用或者弱引用。

        <3>如果block从堆上移除

        会调用block内部的dispose函数

        dispose函数内部会调用 _Block_object_dispose函数

        _Block_object_dispose函数会自动释放引用的auto变量

四:block 变量的类型

        Block变量有3种类型,可以通过调用class 方法或者isa指针查看具体类型,最终都是继承自 NSBlock类型。

block类型在内存中的分布

(1)__NSGlobalBlock__ (_NSconcreateGobalBlock)

        当没有访问自动变量的blcok 都属于这种类型。 这种类型的block变量会存放在内存中的数据区域。

        对该类型调用copy 方法,不会产生任何作用。

(2)__NSStackBlock__. (_NSConcreteStackBlcok)

        访问了局部变量的block属于这种类型,这种类型的blcok变量会存放在内存中的栈中。对该类型调用copy函数后,会变成__NSMallocBlock__类型的block。

(3)__NSMallocBlock__  (_NSConcreteMallocBlock)

当__NSStackBlock__类型的blcok调用了copy函数后会成为这种类型的block。这种类型block存放在内存的堆中。

对该类型的block调用copy函数后,会使blcok对象应用计数加1.

五:ARC环境下 block的copy 

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

(1)block作为函数返回值时

(2)将block赋值给__strong 指针时

(3)block 作为cocoa API中方法名中含有usingBlock的方法参数时

(4)block作为GCD API的方法参数时

ARC下block属性的建议写法

@property (strong, nonatomic) void (^block)(void);

@property (copy, nonatomic) void (^block)(void);

六:block 修饰符

(1)_block可以解决block内部无法修改auto变量值的问题

(2)__block不能修饰全局变量,静态变量(static)

(3)编译器会将__blcok变量包装成一个对象。

        <1>当block在栈上时,并不会对__block变量产生强引用

        <2>当blcok被copy到堆时,

            会调用block内部的copy函数

            Copy函数内部会调用_Block_object_assign 函数

            _Block_object_assign函数会对__block变量形成强引用

            当block从堆中移除时

             会调用block内部的dispose函数

            Dispose函数内部会调用_Block_object_dispose函数

            _Block_object_dispose 函数会自动释放引用的__block 变量

六:__block的__forwarding 指针

(1)当block在栈上时,__forwarding指针指向自身。

(2)当block复制到堆上之后,栈中的block的__forwarding指针会指向堆上的__block变量,被复制到堆上的block的__forwarding指针仍旧指向自身。 

block的forwarding指针

七:blcok的循环引用问题

ARC下解决方案 MRC下解决方案

相关文章

网友评论

      本文标题:Objective-C 语言特性之block

      本文链接:https://www.haomeiwen.com/subject/cxjrhltx.html