深入研究block

作者: charlotte2018 | 来源:发表于2017-08-05 20:30 被阅读59次

最近在看SDWebImage的源码。发现block这块的知识很深,源码里用的有些地方不知道什么作用。所以研究了下。

深层分析Block的实质:它是Objective-C对象。

block 实际上就是 Objective-C 语言对于闭包的实现,闭包就像是C++中的函数指针。为什么说Block其实就是Objective-C对象,因为它的结构体中含有isa指针。


1857952-1a62ba863ebfcf6e.jpg

通过该图,我们可以知道,一个 block 实例实际上由 6 部分构成:
isa 指针,所有对象都有该指针,用于实现对象相关的功能。
flags,用于按 bit 位表示一些 block 的附加信息,本文后面介绍 block copy 的实现代码可以看到对该变量的使用。
reserved,保留变量。
invoke,函数指针,指向具体的 block 实现的函数调用地址。
descriptor, 表示该 block 的附加描述信息,主要是 size 大小,以及 copy 和 dispose 函数的指针。
variables,capture 过来的变量,block 能够访问它外部的局部变量,就是因为将这些变量(或变量的地址)复制到了结构体中。

block的分类

NSConcreteGlobalBlock全局Block

全局Block存储在全局区。在编译期间就已经决定了,如同宏一样。
没有用到外界变量,或者只用到全局变量、静态(static)变量的block就是全局block。
对于全局block,有没有指针引用都不影响。

D61F5E3D-C056-4ECC-ADAD-CCEC8612D6A6.png

NSConcreteStackBlock栈Block

生命周期由系统控制,函数返回即销毁
用到局部变量、成员属性\变量,且没有强指针引用的block都是栈block


屏幕快照 2017-08-05 下午3.58.04.png

我们已经知道了NSConcreteStackBlock,那么它和NSConcreteGlobalBlock有什么区别呢?难道仅仅是引用了外部变量与否的区别吗?答案是否定的.
其实NSConcreteStackBlock内部会有一个结构体__main_block_impl_0,这个结构体会保存外部变量,使其体积变大。而这就导致了NSConcreteStackBlock并不像宏一样,而是一个动态的对象。而它由于没有被持有,所以在它的内部,它也不会持有其外部引用的对象。
证明一下

屏幕快照 2017-08-05 下午4.14.33.png

看到了吧,引用计数没变,发现指针的地址&obj在block中的和在block外的不一样。验证了__main_block_impl_0,这个结构体会保存外部变量。

NSConcreteMallocBlock堆Block

NSConcreteMallocBlock其实就是一个栈block被copy时,将生成NSConcreteMallocBlock。

屏幕快照 2017-08-05 下午4.34.05.png

NSConcreteMallocBlock是会持有外部对象的

屏幕快照 2017-08-05 下午4.40.41.png

看到了吧,只要这个NSConcreteMallocBlock存在,内部对象的引用计数就会+1。

__block 关键字

屏幕快照 2017-08-05 下午4.48.20.png

没错,前文说过,block引用外部是以捕获的形式来捕捉的,而没有声明__block,则会将外部变量copy进block,若用了__block,则是复制其引用地址来实现访问。这就是为什么声明了__block,在block内部改变就会对外有影响的原因了。一个是值传递,一个是引用传递。

注意!!这里需要知道的是,在MRC环境下,如果没有用__block,会对外部对象采用copy的操作,而用了__block则不会用copy的操作。

屏幕快照 2017-08-05 下午4.58.51.png

从更底层的角度来说,在MRC环境下,__block根本不会对指针所指向的对象执行copy操作,而只是把指针进行的复制

而在ARC环境下,对于声明为__block的外部对象,在block内部会进行retain,以至于在block环境内能安全的引用外部对象,所以要谨防循环引用的问题!

ARC下的 block

大家普遍会认为ARC下不存在NSConcreteStackBlock,这是因为本身我们常常将block赋值给变量,而ARC下默认的赋值操作是strong的,到了block身上自然就成了copy,所以常常打印出来的block就是NSConcreteMallocBlock了。所以在ARC下,大部分的应用场景下,几乎可以说是全部都为NSConcreteMallocBlock或者是NSConcreteGlobalBlock。

屏幕快照 2017-08-05 下午5.24.10.png

关于block作为属性用什么修饰。其实用copy和strong都可以的,strong修饰其实也是copy。但是苹果建议我们用copy。我一我们一般写都用copy修饰block。

block 的循环引用问题。

我们知道NSConcreteMallocBlock是会持有外部变量的,而此时如果它所持有的外部变量正好又持有它,就会产生循环引用的问题。

屏幕快照 2017-08-05 下午5.20.56.png

self强引用myBlock, myBlock强引用了self,所以导致self无法释放。

你可以用__weak(ARC)或__block(MRC)来解决:

屏幕快照 2017-08-05 下午5.30.16.png

block对于以参数形式传进来的对象,不会强引用

屏幕快照 2017-08-05 下午5.34.59.png

哈哈没看到有黄色的⚠️。所以不会强引用。

扩展文献

深入研究Block捕获外部变量和__block实现原理
谈Objective-C block的实现

相关文章

  • iOS~Block 笔记

    推荐文章深入研究Block捕获外部变量和__block实现原理Automatic Reference Counti...

  • iOS 内存管理

    深入研究Block捕获外部变量和__block实现原理 http://ios.jobbole.com/88406...

  • Objective-C--Block

    参考文章:深入研究Block捕获外部变量和__block实现原理 Block是什么? Blocks是C语言的扩充功...

  • Block 深入研究

    Block的本质 首先,我们利用clang 命令查看一下声明Block对应的c++代码( xcrun -sdk i...

  • block深入研究

    1、写一个OC文件.m文件如下: 2、使用clang命令将.m编译成.cpp文件,命令如下: 3、打开.cpp,可...

  • 深入研究block

    最近在看SDWebImage的源码。发现block这块的知识很深,源码里用的有些地方不知道什么作用。所以研究了下。...

  • 深入研究Block

    摘选自:[http://www.jianshu.com/p/ee9756f3d5f6] Blocks是C语言的扩充...

  • iOS中的Block一

    参考文章 《Objective-C高级编程》Blocks深入研究Block捕获外部变量和__block实现原理谈谈...

  • iOS当用weak来修饰block时,会发生什么?

    之前验证过copy修饰的block,编译器做了什么。详见文章通过__block的作用深入研究block。与内存有关...

  • iOS Block 相关

    1.深入研究Block用weakSelf、strongSelf、@weakify、@strongify解决循环引用...

网友评论

    本文标题:深入研究block

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