功能优化

iOS Block 语法及底层实现

2020-05-05  本文已影响0人  Damon_Rao

前言

        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

上一篇下一篇

猜你喜欢

热点阅读