美文网首页功能优化
iOS Block 语法及底层实现

iOS Block 语法及底层实现

作者: Damon_Rao | 来源:发表于2020-05-05 18:35 被阅读0次

    前言

            iOS开发的同学们肯定都用过block,对block 的运用熟练在我们开发过程中很有必要;在此我也对其进行一番解读,希望能在大家开发或者面试过程中有所帮助。

    一、block 概要

    1、什么是block

    一句话:带有自动变量(局部变量)的匿名函数。 

    Q1 什么 是匿名函数  度娘解释曰 ;

    Q2 自动变量相关:C语言函数中可能用到的变量由自动变量、函数参数、静态变量、静态全局变量、全局变量;有兴趣的还可以去探究其内存中的存储域

    二、block用法定义

    1、block语法及变量

    block 语法: ^ 返回值类型 参数列表 表达式 

    ^ (NSString *) (NSString *bookId){}

    省略返回值写法:

    ^(void){};

    省略返回值及参数写法:

    ^{};

    2、block 实质

    简单些一行block

    声明一个block并打印

     void(^block)(void)=^{

            printf("block block \n");

        };

        block();

    通过在终端命令行 clang -rewrite-objc 源代码文件名 ,会生成相应的.cpp文件,由C++编写的源文件。具体实现大家可以自己去操作一下我这里贴出一段类似代码:

    C++ 源码文件中很多内容,只要看到截图的调用部分。

    block实质也是一个oc对象,简单来说和class代码的设计思想是为其构建一个结构体struct,把结构体相应参数做索引,遵循一定的规则,去找到相应的实现方法;同样的我们在设计OC 对象时也应借助这个思想。无论对象怎么变化,他有一个基本的结构体单元。

    三、block截获自动变量

    1、block底层如何截获自动变量值

    上文中讲到block实际是一个oc 对象,它就有isa指针、变量等对象属性,当执行block语句中有使用外部变量时,会将相应的变量值 自动声明并存储到block 的结构体当中去

    2、blcok 捕获自动变量

    __block 修饰词修饰变量,类似于static、auto 等c语言声明词,一经修饰,底层会做相应的处理;至于做了什么,要跟blcok 存储域有关。blcok类型 有三种 

    栈上 NSConcreteStackBlock

    堆上 NSConcreteMallocBlock

    全局 NSConcreteGlobalBlock (存储在程序的数据区)

    当使用的是栈上的block 时,你使用的又是局部变量,想截获变量改变其值,必须用__block 修饰,是将该变量变为一个结构体自动变量才能被修改,示例如下:

    block_t=blk;

    {

    __block NSMutableArray *array=[[NSMutableArray alloc] init];

    blk=^(NSObject obj){

        [array add object:obj];

    }

    blk([[NSObject alloc] init]);

    }

    或者这么写:

    block_t=blk;

    {

     NSMutableArray *array=[[NSMutableArray alloc] init];

    blk=[^(NSObject obj){

    [array add object:obj];

    } copy]

    blk([[NSObject alloc] init]);

    }

    这两种写法 都是 将变量array 从 栈copy 到 堆上,第一种是array 变量通过 block 修饰符生成一个forwarding指针,指向其拷贝到堆上的生成的自动变量结构体;第二种是把block 拷贝到堆上截获的变量也会被拷贝到堆上,这样当运行完大括号的代码时,array 也不会被释放,知道block 运行完成被释放时,array 才会跟着被释放释放是由系统调用dispose完成。

    关于对象类型自动变量捕获,使用方法一还是方法二有个小总结:

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

    2)将block赋值给类的附有__strong 修饰符的id 类型或block类型成员变量时

    3)向方法名中含有usingBlock 的cocoa框架方法或GCD 的api 传递block 时;

    除了以上几种情况外,其他都建议使用方法二。

    由于以上变量都是 strong 类型变量,如果是weak 类型变量呢?类似如下:

    block_t=blk;

    {

    __block NSMutableArray __weak *array=[[NSMutableArray alloc] init];

    blk=^(NSObject obj){

        [array add object:obj];

    }

    blk([[NSObject alloc] init]);

    }

    经验证,运行完代码,在作用域外array 会被释放,array 不会有任何改变。

    3、blcok循环引用

    block 的循环引用是我们平时写代码经常要注意的问题,循环引用的原因即为 block 强持有该 自动变量,自动变量又强持有 blcok,运行完后释放时你等待我释放,我等待你释放,由此造成死循环;类似的有多线程中的死锁问题,二等待一运行完一等待二运行完导致两者都在等待执行卡死在那儿。如下图

    循环引用示意图

    避免循环引用的方式主要是两种:

    1)使用__weak 或 __unsafe_unretained修饰自动变量使得blcok 弱持有 自动变量,当block 执行完后,blcok 和对象都能被释放

    2)使用__block 修饰自动变量 , 但是 必须要调用 一次 block ,才能使得 block变量对 自动变量的持有释放,不然还是会有循环引用

    4、blcok 拷贝/释放

    有一个小点,显示调用copy 和release 的情况,是在非ARC 情况下即需要我们主动去调用release 方法 将copy 到堆里的block 释放掉,这里我不做详细描述,有兴趣的可以自己去度娘问问;

    block 的使用博大精深,需要更进一步的探寻它的使用场景,期待与你一起深入学习研究。。。

    参考:

    1、<<Object-C 高级编程iOS 与OSX 多线程和内存管理>>

    2、https://www.jianshu.com/p/6a7c498c42bc

    相关文章

      网友评论

        本文标题:iOS Block 语法及底层实现

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