一:什么是block?
block的是本质是对象(万物皆对象)。但是你也可以说它是代码块、闭包、内联函数、函数指针...还有很多叫法,也可能这里的叫法都是错误的,或是不准确的,但是我个人觉得从功能上讲,可以这么理解。不过为了对oc的尊敬,还是叫它block吧,block就是block。在其他语言中也有类似block的语法,像javascript的闭包,函数里面的函数,java中的代码块,c中的函数指针等。就好像,事先放一段代码在这里,然后需要的时候回过头来调用。我们知道,代码执行是按顺序调用的,也就是我们常说的面向过程。但是block可以反向调用。只不过block可以写在函数里面,也可以说它是函数中的函数,它是比较特殊的函数。请看下图,block 简单实现:

【注】:block 其实保存了一个代码块,随时随地可以调用
二:block写法:
先上大师总结:

下边是几种常用的写法(自己写的,完全和大师的比不了,比不了【斜眼笑】):


三:block 分类:
__NSGlobalBlock 在数据区 (数据区一般的开头都是:0x1开头)
__NSMallocBlock 在堆区 (堆区一般开头都是 : 0x6 开头)
__NSStackBlock 在栈区 (栈区一般开头都是: 0x7开头)
【注】:如果内存方面有什么不懂的,可以先看看我的这篇文章:https://www.jianshu.com/p/c247feff5969
请看例子:
MRC 模式下:




ARC模式下:



由上边的例子可以知道:
- StackBlock 是GlobalBlock 引入了外部变量
- MallocBlock 会对外部对象进行强引用,而stackBlock 不会
- MallocBlock 是StackBlock copy 来的(ARC 模式下有写不同)
- 定义别名的GlobalBlock写法,当引入外部变量,MRC模式下为StackBlock,而ARC模式下为MallocBlock
- 在实际使用中:ARC 模式其实就剩下2种类型了,stackBlock 基本上不会出现
三:block MRC 和 ARC 下有什么不同
上边的第4条和第5条其实就是不同点,但不是全部,让我们接着探究。。。
-
block 作为属性的时候,MRC使用copy 关键字修饰,ARC使用copy or strong 都可以。why?
详见:https://www.jianshu.com/p/de1beba9958e -
在解决循环引用的时候,MRC使用__block 而ARC使用的__weak,why?
因为ARC模式下使用__blcok 有时候还是会循环引用
四:block 在日常开发中的坑
1. 调用外部变量和常量
MRC模式下:




ARC 模式居然和MRC一样,这个让我有点惊讶(以为会有不同呢)!!!
由上边的例子可以得出以下结论
- block 有“捕获”属性;可以捕获外部变量(一般情况是捕获变量的值)。
- block里边可以使用局部变量的值,但是不能修改局部变量。
- block里边局部变量的地址值和外边是不一样的(猜想block捕获了局部变量的值,在自己的作用域里边自己保存了一份)。
- block里边可以修改全局变量,__block 或者 static 修饰的局部变量。
- block里边全局变量,__block 或者 static 修饰的局部变量 的地址不变(猜想把变量的地址传进去了,所以地址是一样的,并且可以修改它们的值)。
那么我们怎么证明这些猜想是否正确呢?来来来,让我们源码走一波。。。
- 在main.m 文件里边写上代码:

- 终端执行 clang -rewrite-objc main.m 得到 main.cpp 文件(其实是C++文件)。
打开这个main.cpp文件,在最下边找到block 的c++实现代码,如图:

由C++的实现代码可以知道:
- block 直接捕获局部变量的值,在自己的作用域里边开辟一块区域保存这个值(所以局部变量不能在block 里边修改值,换句话说:block里边值修改了,外边的变量也不知道(不是一个作用域),所以不能修改局部变量的值)。
- block默认捕获的是值,__block修饰以后,局部变量变成了一个结构体,block捕获的是结构体的指针,所以可以修改。
【注】:原来打算这里分析一波c++ 代码呢,但是感觉东西有点多(block 结构体含义,block内部的实现==),准备单独写一篇block底层分析,感兴趣的同学可以看看这篇,这里就不多说了。
2. 循环引用:
老规矩,上代码:

局部变量的block,在方法走完的时候就释放了,block不管是否引用self,这个ViewController 都能正常的 dealloc
这2种是常见的使用,也很不容易写错。下边是几种编译器识别不了的:

其实循环引用的根源就是block是否被当前类所持有,而block里边是否有持有了当前类,只要这2个不形成一个闭环就不会产生循环引用。
网友评论