Block(四)对象类型的auto变量
00 分钟
2022-11-11
2022-11-11
type
status
date
slug
summary
tags
category
icon
password
我们在针对Block的剖析在进一步加深,了解了Block如何截获基本类型,了解Block的类型和copy操作,下面我们开始进入Block的内存管理世界
Block的内存管理,主要针对捕获外部对象类型的auto变量。
根据Block捕获auto基本变型的规律,针对对象,仍然适用
  • auto变量捕获后,Block中变量的类型和变量原类型一致;
  • static变量捕获后,Block对应的变量是对应变量的指针类型;
那么,auto对象与基本类型在Block内部有什么区别呢。
我们将从两方面讨论:
  1. Block是如何捕获对象类型的?
  1. Block内部是如何管理对象类型的?

一、捕获对象类型

以下代码位于Block捕获对象类型-Test.m中。

1. Block对象结构体

将下面代码重写:
以上Block对象重写后的结构体如下:
可以看到:Block对象仍然捕获auto变量后,保留了person对象的类型。

2.DescFunc

进一步观察Block对象中的Desc结构以及Func
那么与基本类型的捕获区别在哪里呢?
我们观察与之前基本类型的区别,Func基本没有区别,Desc有区别,多了两个函数指针:
  • void (*copy)
  • void (*dispose)
那么继续查看这两个函数:
针对这两个函数,它们的作用就是:
函数
作用
调用时机
__Test__test_block_copy_0
调用 _Block_object_assign,相当于retain,将对象赋值在对象类型的结构体变量 __Test__test_block_impl_0中。
栈上的Block复制到堆时
__Test__test_block_dispose_0
调用 _Block_object_dispose,相当于release,释放赋值在对象类型的结构体变量中的对象。
堆上Block被废弃时

二、内存管理

在我们观察了Block对象捕获对象类型的内部结构之后,我们基本就能了解Block内部是如何管理的:
  • 在栈上的Block对象复制到堆上,对person进行retain
  • 在堆上Block被废弃时,对person进行废弃;
以上操作在ARC环境下,由系统帮助我们完成,但在MRC下,我们仍然要自己管理。
下面,我们就一步一步探索验证不同情境下的对象类型内存管理。

2.1 Block在栈上

我们将项目调成MRC环境,并在类BFPerson重写dealloc
测试如下代码:
notion image
可以看到,在[person release]后,Block内部再次访问直接崩溃,说明Block内并没有对person对象进行强引用,使得person在内存中释放。
总结:
在栈空间,Block不会对auto变量进行强引用。

2.2 Block在堆上

2.1 Block强引用对象类型

我们将2.1,即上节中的代码,在ARC环境下再次试验,打印结果如下:
Block捕获对象类型[94670:4794884] begin
  • *Block捕获对象类型[94670:4794884] class: _NSMallocBlock*
Block捕获对象类型[94670:4794884] age 28
Block捕获对象类型[94670:4794884] end
Block捕获对象类型[94670:4794884] BFPerson delloc
针对以上结果:
  1. ARC情况下,在Block赋值给__strong指针时,栈上的Block自动拷贝到堆;
  1. 拷贝的同时会将person对象进行retain操作,在这里相当于强引用;
  1. 调用block时,即使person出了大括号,系统会release一次,但由于block内部仍然强引用person,所以不会销毁;
  1. 在打印"end"后,block销毁,那么对person将进行一次release操作,所以person对象销毁。

2.2 Block弱引用对象类型

我们将block内部引用的auto变量改为__weak,我们知道__weak表示的是弱引用指针。
上面代码输出的测试结果如下:
Block捕获对象类型[94923:4823185] begin
  • *Block捕获对象类型[94923:4823185] class: _NSMallocBlock*
Block捕获对象类型[94923:4823185] BFPerson delloc
Block捕获对象类型[94923:4823185] age 0
Block捕获对象类型[94923:4823185] end
从输出结果我们很明显的看出,只要出了大括号,person对象就被销毁了,此后block调用,获取到的结果是0,这其实并不是person对象的age值。
我们将上面代码重写为C++代码,看看Block内部到底发生了什么?
此时发生错误:
notion image
针对上面问题,我们制定ARC下的运行时系统版本即可:
得到的Block对象结构体:
其实上面大体和强引用对象类似,只是其对应copydispose函数针对强引用和弱引用有所不同。

三、 总结

notion image

参考

示例代码

  1. Block捕获对象类型