小码哥iOS学习笔记第九天: block的类型

一、block的类型

  • block3种类型,可以通过调用class方法或者isa指针查看具体类型,最终都是继承自NSBlock类型

    • __NSGlobalBlock__ ( _NSConcreteGlobalBlock )
    • __NSStackBlock__ ( _NSConcreteStackBlock )
    • __NSMallocBlock__ ( _NSConcreteMallocBlock )
  • 这三种类型在内存中分别存储在不同的区域

    • __NSGlobalBlock__存在于内存的数据区域(.data区)
    • __NSStackBlock__存在于内存的栈区
    • __NSMallocBlock__存在于内存的堆区

三种类型的block有什么区别?

二、block的三种类型

  • 首先将程序设置成MRC模式(ARC在后面)

1、__NSGlobalBlock__
  • 内部没有使用auto类型变量的block, 就是__NSGlobalBlock__类型

2、__NSStackBlock__
  • 内部使用了auto类型变量的block, 就是__NSStackBlock__类型

3、__NSMallocBlock__
  • __NSStackBlock__类型的block调用copy后就是__NSMallocBlock__类型, 通过copy, 将block从栈区复制到了堆区

  • __NSGlobalBlock__类型的block调用copy后类型不变, 还是__NSGlobalBlock__类型(还在数据区)

  • __NSMallocBlock__类型的block调用copy后类型不变, 还是__NSMallocBlock__类型(不会生成新的block, 原有引用计数+1)

4、block类型总结

三、ARC环境下, block的类型问题

  • 将程序设置回ARC环境

  • 在ARC环境下, 编译器会根据情况自动将栈上的block复制到堆上, 比如以下情况
1、__NSStackBlock__类型的block做为函数返回值时, 会将返回的block复制到堆区

2、将__NSStackBlock__类型的block赋值给__strong指针时, 会将block复制到堆区

  • 如果没有__strong指针引用__NSStackBlock__类型的block, 那么block的类型依然是__NSStackBlock__

3、block作为Cocoa API中方法名含有usingBlock的方法参数时, block在堆区
  • Cocoa API中会有一些方法, 比如数组的遍历方法

4、block作为GCD API的方法参数时, block在堆区

以上四种方式的block, 都会被复制到堆区

5、__NSGlobalBlock__类型的block, 不管怎样类型都不会改变, 依然在数据区

四、对象类型的auto变量

  • 创建一个类Person, 并添加一个age属性, 重写dealloc方法

1、MRC情况下, 对象类型的auto变量
  • 将程序设置为MRC

  • 我们知道, 一个局部的对象变量在离开作用域时会被释放

  • 如果栈区的block中使用了对象类型的auto对象, 那么auto对象离开作用域还会被释放吗?

  • 根据结果可以知道, 处于栈区中的block, 如果引用了auto对象, 当auto对象离开作用域时一样会被释放
  • 如果将栈区的block复制到堆上,又是什么样的结果?

  • 根据结果可以知道, 如果将block复制到堆上, 此时person对象没有被释放,说明block在复制到堆上时对person进行了一次retain处理
  • 如果我们释放掉堆中的block, 可以看到person也被释放了

  • 说明, 当block从堆中释放时, 会对其中引用的aotu对象变量进行一次release处理

总结:
栈中的block不会将对象类型的auto变量进行retain处理, 只有在将block复制到堆上时, 才会将对象类型的auto变量进行retain处理(引用计数+1)
当堆中的block释放时, 会对其中的对象类型的auto变量进行release处理(引用计数-1), 如果此时对象类型的auto变量的引用计数为零, 就会被释放

2、ARC情况下, 对象类型的auto变量
  • 将程序设置回ARC环境

  • 在ARC下, 局部的对象变量离开作用域时, 一样会被释放

  • 此时在block中使用auto变量, 可以看到person离开作用域并没有被释放

  • 只有当block离开作用域被释放时, person才被释放

  • 这主要是因为在ARC下, 一个__NSStackBlock__类型的block被一个__strong类型的指针引用时, 系统会将block自动复制到堆区
  • 此时的block类型是__NSMallocBlock__, 会对person进行一次强引用(类似MRC下的retain操作)

3、查看block底层结构
  • 使用终端, cdmain.m文件所在文件夹, 并执行命令
xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc -fobjc-arc -fobjc-runtime=ios-8.0.0 main.m
复制代码
  • 将生成的main.cpp文件拖到项目中打开

  • 可以看到, __main_block_impl_0中捕获到了personauto变量, 并且是一个__strong类型

  • 并且, 在__main_block_desc_0中也多了两个函数指针copydispose

  • 这两个函数指针传入参数是__main_block_copy_0__main_block_dispose_0两个函数的地址

  • 实际上, 当block被复制到堆上时,会调用__main_block_copy_0函数, 来对捕获的对象类型的auto变量进行强引用
  • block从堆上移除时, 又会被调用__main_block_dispose_0函数, 对捕获的对象类型的auto变量解除强引用
  • 而在栈上的block, 即使捕获了对象类型的auto变量,也不会调用__main_block_copy_0函数和__main_block_dispose_0函数, 即不会对对象类型的auto变量进行强引用
4、__weak
  • ARC下, 程序提供了__weak关键字, 用来修饰对象类型的auto变量
  • block捕获到的对象类型的auto变量__weak修饰时, 即便block被复制到了堆上, __main_block_copy_0方法也不会对被捕获的对象类型的auto变量进行强引用

  • 此时block的底层结构如下:

  • 此时__main_block_desc_0中依然有copydispose, 只不过不在对person进行强引用

总结:
不论在ARC还是MRC下,栈中的block不会对捕获到的对象类型auto变量进行强引用(引用计数+1), 只会在copy到堆中时, 会对对象类型auto变量进行强引用
ARC下, 被__weak修饰的对象类型auto变量, 在block复制到堆中时不会进行强引用

转载于:https://juejin.im/post/5c81144bf265da2dd42797dd

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值