//定义一个Block
typedef returnType(^BlockName)(parameterA,parameterB,...)
e.g:typedef void(^RequestResult)(BOOL Result)
//实例
^{NSLog(@"This is a Block");}

Block本质是一个OC对象,内部含有一个isa指针,它是一个封装了函数及函数调用环境的OC对象,可以添加到NSArray及NSDictionary等集合中,它是基于C语言及运行时特性,有点类似标准的C函数。但除了可执行代码以外,另外包含了变量同堆或栈的自动绑定。

常用介绍

Block的类型:

1、NSGlobalBlock

void(^exampleBlock)(void)=^{//block
}
NSLog(@"exampleBlock is:%@",[exampleBlock class])

打印日志:exampleBlock is:__NSGlobalBlock

如果一个block没有访问外部局部变量,或者访问的全局变量,或者静态局部变量,此时的block就是一个全局block,并且数据存储的全局区。

2、NSStackBlock

int temp = 100
void(^exampleBlock)(void) = ^{//NSLog(@"exampleBlock is:%d, temp")
}
NSLog(@"exampleBlock is:%@,[exampleBlock class]")

打印日志:exampleBlock is: __NSMallocBlock__???不是说好的 __NSStackBlock__的吗?为什么打印的是__NSMallocBlock__ 呢?这里是因为我们使用了 ARC ,Xcode 默认帮我们做了很多事情。

我们可以去 Build Settings 里面,找到 Objective-C Automatic Reference Counting,并将其设置为 No ,然后再 Run 一次代码。你会看到打印日志是:exampleBlock is: __NSStackBlock__

如果 block 访问了外部局部变量,此时的 block 就是一个栈 block ,并且存储在栈区。由于栈区的释放是由系统控制,因此栈中的代码在作用域结束之后内存就会销毁,如果此时再调用 block 就会发生问题,( 注: 此代码运行在 MRC 下)如:

void (^simpleBlock)(void);
void callFunc() {int age = 10;simpleBlock = ^{NSLog(@"simpleBlock-----%d", age);};
}int main(int argc, char * argv[]) {NSString * appDelegateClassName;@autoreleasepool {callFunc();simpleBlock();// Setup code that might create autoreleased objects goes here.appDelegateClassName = NSStringFromClass([AppDelegate class]);}return 0;
}

打印日志:simpleBlock--------41044160

3、NSMallocBlock

当一个__NSStackBlock__类型block做copy操作后,就会将这个block从栈上复制到堆上,而堆上的这个block类型就是__NSMallocBlock__类型。在ARC环境下,编译器会根据情况,自动将block从栈上copy到堆上。具体会进行copy的情况有如下4种:

1、block作为函数的返回值时;

2、block赋值给__strong指针,或者赋值给block类型的成员变量时;

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

4、block作为GCD API的方法参数时;

__Block的作用

简单来说,__block作用是允许block内部访问和修改外部变量,在ARC环境下还可以用来防止循环引用。

__block int num = 100;
void(^exampleBlock)(void) = ^{NSLog(@"1、num is:%d",num);num = 120;NSLog(@"2、num is:%d",num);
};
exampleBlock();
NSLog(@"3、num is:%d",num);

__block主要用来解决block内部无法修改auto变量值的问题,为什么加上__block修饰之后,auto变量值就能修改呢?

这是因为,加上__block修饰之后,编译器会将__bl;ock变量包装成一个结构体__Block_byref_num_0,结构体内部的*__forwarding是指向自身的指针,并且结构体内部还存储着外部auto变量。

struct __Block_byref_val_0{
void *__isa;//isa指针
__Block_byref_val_0 *__forwarding;
int __flags;
int __size;//Block结构体大小
int num;//捕获到的变量
}

从上图可以看到,如果 block 是在栈上,那么这个 __forwarding 指针就是指向它自己,当这个 block 从栈上复制到堆上后,栈上的 __forwarding 指针指向的是复制到堆上的 __block 结构体。堆上的 __block 结构体中的 __forwarding 指向的还是它自己,即 num->__forwarding 获取到堆上的 __block 结构体,num->__forwarding->num 会把堆上的 num 赋值为 120。因此不管是栈上还是堆上的 __block 结构体,最终使用到的都是堆上的 __block 结构体里面的数据。

  • __weak 的作用

简单来说是为了防止循环引用。

self 本身会对 block 进行强引用,block 也会对 self 形成强引用,这样就会造成循环引用的问题。我们可以通过使用 __weak 打破循环,使 block 对象对 self 弱引用。

此时我们注意,由于 block 对 self 的引用为 weak 引用,因此有可能在执行 block时,self 对象本身已经释放,那么我们如何保证 self 对象不在 block 内部释放呢?这就引出了下面__strong 的作用。


  • __strong 的作用

简单来说,是防止 block 内部引用的外部 weak 变量被提前释放,进而在 block 内部无法获取 weak 变量以继续使用的情况;

__weak __typeof(self) weakSelf = self;
void (^exampleBlock)(void) = ^{__strong __typeof(weakSelf) strongSelf = weakSelf;[strongSelf exampleFunc];
};

这样就保证了在 block 作用域结束之前,block 内部都持有一个 strongSelf 对象可供使用。

但是,即便如此,依然有一个场景,就是执行 __strong __typeof(weakSelf) strongSelf = weakSelf; 之前,weakSelf 对象已经释放,这时如果给 self 对象发送消息,这没有问题,Objective-C 的消息发送机制允许我们给一个 nil 对象发送消息,这不会出现问题。但如果有额外的一些操作,比如说将 self 添加到数组,这时因为 self 为 nil,程序就会 Crash。

我们可以增加一层安全保护来解决这个问题,如:

__weak __typeof(self) weakSelf = self;
void (^exampleBlock)(void) = ^{__strong __typeof(weakSelf) strongSelf = weakSelf;if (strongSelf) {// Add operation here}
};

拓展知识

  • 思考题

Block 内修改外部 NSMutableString 、NSMutableArray 、NSMutableDictionary 对象,是否需要添加 __block 修饰?

NSMutableArray *mutableArray = [[NSMutableArray alloc] init];
[mutableArray addObject:@"1"];
void (^exampleBlock)(void) = ^{// block[mutableArray addObject:@"2"];
};
exampleBlock();
NSLog(@"mutableArray: %@", mutableArray);

打印日志:

mutableArray: ( 1, 2 )

答案是:不需要,因为在 block 内部,我们只是使用了对象 mutableArray 的内存地址,往其中添加内容。并没有修改其内存地址,因此不需要使用 __block 也可以正确执行。当我们只是使用局部变量的内存地址,而不是对其内存地址进行修改时,我们无需对其添加 __block ,如果添加了 __block 系统会自动创建相应的结构体,这种情况冗余且低效。

  • Block 数据结构

Block 内部数据结构图如下:

struct Block_descriptor {unsigned long int reserved;unsigned long int size;void (*copy)(void *dst, void *src);void (*dispose)(void *);
};struct Block_layout {void *isa;int flags;int reserved; void (*invoke)(void *, ...);struct Block_descriptor *descriptor;/* Imported variables. */
};

Block_layout 结构体成员含义如下:

isa: 指向所属类的指针,也就是 block 的类型

flags: 按 bit 位表示一些 block 的附加信息,比如判断 block 类型、判断 block 引用计数、判断 block 是否需要执行辅助函数等;

reserved: 保留变量;

invoke: block 函数指针,指向具体的 block 实现的函数调用地址,block 内部的执行代码都在这个函数中;

descriptor: 结构体 Block_descriptor,block 的附加描述信息,包含 copy/dispose 函数,block 的大小,保留变量;

variables: 因为 block 有闭包性,所以可以访问 block 外部的局部变量。这些 variables 就是复制到结构体中的外部局部变量或变量的地址;

Block_descriptor 结构体成员含义如下:

reserved: 保留变量;

size: block 的大小;

copy: 函数用于捕获变量并持有引用;

dispose: 析构函数,用来释放捕获的资源;


总结

使用 Block 过程中需要我们关注的重点有 4 个:

  1. block 的三种类型;

  2. block 避免引起循环引用;

  3. block 对 auto 变量的 copy 操作;

  4. __block、__weak、__strong 的作用;

iOS中Block的使用注意事项相关推荐

  1. c语言block内部的实现原理,iOS中block变量捕获原理详析

    Block概述 Block它是C语言级别和运行时方面的一个特征.Block封装了一段代码逻辑,也用{}括起,和标准C语言中的函数/函数指针很相似,此外就是blokc能够对定义环境中的变量可以引用到.这 ...

  2. 函数遍历IOS中block的使用

    最近研究函数遍历,稍微总结一下,以后继续补充: block是ios4.0开始推出的,其特点是可以执行内联,作为参数传递到函数,block变量的定义,^ 用于运算符. -(id)loadNibWithC ...

  3. iOS中block的详解weakSelf、strongSelf-转自唐巧

    1 我们知道,在使用 block 的时候,为了避免产生循环引用,通常需要使用 weakSelf 与 strongSelf,写下面这样的代码: __weak typeof(self) weakSelf ...

  4. iOS中Block的基础用法

    原文链接:http://www.jianshu.com/p/17872da184fb 本文简介 本章不会对Block做过多的实现研究.只是讲解基本的用法.纯粹基础知识.结合实际项目怎么去做举例.Blo ...

  5. 【iOS】—— iOS中的相关锁

    文章目录 自旋锁 1.OSSpinLock 2.os_unfair_lock 3.atomic 互斥锁 pthread_mutex @synchronized objc_sync_enter objc ...

  6. [iOS开发]iOS中的相关锁

    锁作为一种非强制的机制,被用来保证线程安全.每一个线程在访问数据或者资源前,要先获取(Acquire)锁,并在访问结束之后释放(Release)锁.如果锁已经被占用,其它试图获取锁的线程会等待,直到锁 ...

  7. iOS开发—block介绍

    - (void)viewDidLoad {[super viewDidLoad];NSLog(@"我在玩手机");NSLog(@"手机没电了");[self c ...

  8. iOS中JS 与OC的交互(JavaScriptCore.framework)

    iOS中实现js与oc的交互,目前网上也有不少流行的开源解决方案: 如:react native 当然一些轻量级的任务使用系统提供的UIWebView 以及JavaScriptCore.framewo ...

  9. iOS之Block总结以及内存管理

    block定义 struct Block_descriptor {unsigned long int reserved;unsigned long int size;void (*copy)(void ...

最新文章

  1. MySQL开启远程连接权限
  2. 独家 | 如何手动优化神经网络模型(附链接)
  3. 祝愿大家都是健康的!
  4. 蓝桥杯java第八届第三题--承压计算
  5. Autodesk 首届中国开发者训练营将开始报名,5月24日前报名6折优惠!
  6. 利用detours实现API劫持
  7. DFT频谱泄漏的数学分析及不产生泄漏的条件
  8. Word 公式编辑器: 公式居中,编号居右,带章节号自动更新,且可以交叉引用
  9. 一份软件工程行业生存指南
  10. 软件开发流程有哪些?完整的软件开发流程
  11. 《期权、期货及其他衍生产品》读书笔记(第三章:利用期货的对冲策略)
  12. python怎么算二元一次方程_用python解决高数所有计算题--sympy求解极限、积分、微分、二元一次方程等...
  13. BEC 高级,成绩A。
  14. LOL服务器维护奖励,LOL5.18版本改动内容 lol官网服务器维护公告
  15. 重构实践:基于腾讯云Elasticsearch搭建QQ邮箱全文检索
  16. Linux下各压缩方式测试(压缩率和使用时间)
  17. 轻松实现在windows平台搭建Nexus私服
  18. CAD坐标有哪些输入方式?来看看这些CAD坐标输入方式!
  19. 明星热图|刘亦菲拍摄品牌大片;唐艺昕、易烊千玺代言新品牌;全智贤、黄景瑜、江疏影演绎品牌新品...
  20. RxHttp-一条链发送请求之强大的数据解析功能(二),kotlin构造器

热门文章

  1. 基于DEM模拟淹没区域随时间推演算法代码展示
  2. Jenkins基础: root URL设定
  3. wfp(微软)对付flash
  4. kpw1 5.6降级
  5. 计算机相关专业宣讲会日程
  6. 软件测试无效bug分析,(一)Bug不能重现的原因分析及其对策
  7. android biz,魔轮(lcb.android.biz) - 2.6.7 - 应用 - 酷安
  8. [网络配置] 使用有线网络连接局域网,使用无线网络连接互联网
  9. 咱们程序要预防腰椎间盘突出
  10. 未查询到服务器角色信息,dcdiag结果如果排查:拥有 PDC 角色的服务器已关闭。无法找到时间服务器。 - 网络管理论坛 - 51CTO技术论坛_中国领先的IT技术社区...