又见block(六):block存储域与__block变量存储域

本文主要讨论block的存储域与__block变量的存储域(即存储位置)

当引用了OC中的Foundation或者UIKit框架时,通过 clang -rewrite-objc 指定文件名 命令将指定文件转换成C++代码会报错,可通过 clang -x objective-c -rewrite-objc -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk 指定文件名

我们知道它们在转换成C++源代码时都被转换成了结构体类型,都是在栈上生成的结构体实例

名称实质
block栈上block的结构体实例
__block变量栈上__block变量的结构体实例

前面我们说到block其实也是一种OC对象,其对应的类以及存储位置有以下几种

类(blockisa类型)存储位置
NSConcreteGlobalBlock程序的数据区域(全局静态区)
NSConcreteMallocBlock堆上
NSConcreteStackBlock栈上

之前我们遇到的都是NSConcreteStackBlock类的block,一旦我们在全局区声明一个block时,例如

#import <stdio.h>
void (^blk) (void) = ^{
};
int main() {
    return 0;
}

将此代码转换成C++源码时,就会发现其isa指向的是NSConcreteGlobalBlock,该block在全局区,不能引用自动变量,所以不存在截获自动变量的情况,结构体实例内容不依赖执行时的状态,整个程序运行过程中只需要一个实例,所以与全局变量一样存储在数据区域(全局静态区)。

总结如下:
1、在全局区域声明定义一个block
2、block表达式中没有使用捕获的自动变量时

以上情况生成的block都是NSConcreteGlobalBlock类型,只生成一个结构体实例;除此情况下,生成的都是NSConcreteStackBlock类型的block,且都是存储在栈上。那么block配置在堆上的NSConcreteMallocBlock类什么时候使用呢?

block存储域

1、block超出变量作用域可存在的原因
2、__block变量中的结构体成员变量__forwarding存在的原因

配置在全局的block在变量作用域可以通过指针安全使用。但是设置在栈上的block如果所属的变量作用域结束,该block就被废弃。同理,__block变量也配置在栈上,如果所属的变量作用域结束,该变量也会被废弃。那么block是如何解决这个问题的呢?
block通过将block和__block变量从栈上复制到堆上的方法来解决这个问题,即使变量作用域结束了,堆上的block__block变量还 可以继续存在。

  • 复制到堆上的block将结构体实例中的成员变量isa设置为_NSConcreteMallocBlock
  • __block变量用结构体成员变量__forwarding可以实现无论__block变量配置在栈上还是堆上都能够正确的访问__block变量

block提供的复制方法是:objc_retainBlock函数,实际上就是_Block_copy函数
在大多数情况下,编译器能自行判断,自动将block从栈上复制到堆上。需要我们使用copy实例方法手动生成代码将block从栈上复制到堆上,主要针对下面这种情况:

  • 当方法或函数的参数中传递的是block

    如果在方法或者函数中复制了传递过来的参数,就无须手动复制了,例如:

  • Cocoa框架中的方法且方法名中含有usingBlock等时
  • GCD中的API
block的类副本源的配置存储位置copy复制效果
NSConcreteGlobalBlock程序的数据区域(全局静态区)什么也不做
NSConcreteMallocBlock引用计数加1
NSConcreteStackBlock从栈复制到堆

__block变量存储域(存储位置)

当使用了__block变量的block从栈上复制到堆上时,__block变量也会受到影响

__block变量的配置存储位置block从栈复制到堆上的影响
从栈复制到堆并被block持有
被block持有

当多个block使用__block变量时,任何一个block从栈复制到堆时,__block变量也会一并从栈复制到堆上并被该block持有,剩下的block从栈复制到堆时,也会持有该__block变量,并增加变量的引用计数,与OC的引用计数式内存管理思想一致。如果所有持有__blockblock都被废弃,__block变量也就会被释放。

通过block的复制,__block变量也从栈复制到堆,此时可同时访问栈上的__block变量和堆上的__block变量

复制__block变量
栈上的__block变量的结构体实例在__block变量从栈复制到堆上时,会将成员变量__forwarding的值替换为复制目标堆上的__block变量的结构体实例地址。
又见block(一):block是什么?
又见block(二):block语法定义
又见block(三):block实质
又见block(四):block捕获自动变量
又见block(五): __block变量和对象
又见block(七):截获对象

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值