实质为,存放指针变量的内存指向对象的内存);所谓的指向,即是对该对象的引用计数加1即retain操作(即此时该指针持有该对象),但是一个对象可能被多个指针持有,因此realease并不必然导致对象销毁(内存回收)而只是被释放即某个指针变量不再指向(持有)该内存区域,只有当对象的引用计数为0时,系统才会自动回收其内存;
注:引用计数为对象的属性,而非指针;
<6>__block修饰的基本数据类型的变量和对象类型变量,block被拷贝时,都会通过调用__test1_block_copy_0函数中_Block_object_assign函数完成对自身的拷贝——其中,_Block_object_assign函数的第三个参数,8表示由__block修饰的基本数据类型变量,3表示访问的事对象类型变量(那么,__block修饰的对象类型是多少呢?往下看);
当block对象销毁时,二者都是通过调用__test1_block_dispose_0函数中的_Block_object_dispose函数来被销毁;
问题:为什么要增加copy和dispose两个函数指针呢?因为block对象要持有上述两种对象(__block修饰的基本数据类型生成的对象和per实例对象),那么自然要对其进行内存管理,达到持有/释放的可控目的;
因此,__block修饰生成的对象(会随着block的copy而一起被copy到堆区,而拷贝后的栈区的结构体依然会存在,只不过其作用域结束后,系统会对其内存自动回收),block对象要对其持有,肯定是强引用,否则弱引用,该对象的内存管理不受控制,那么block内部修改变量的值存在极大风险——这点没问题;
补充:__weak不能修饰基础类型变量
如上,我么知道,__weak仅能修饰对象类型变量和block指针类型——为什么?
以上我们已经分析过,__strong和__weak:前者指针持有对象达到对该对象内存管理可控的目的(只要该对象的引用计数>0,其内存就不可能被回收,指针就可以合法指向该内存),会进行retain操作即对象的retainCount会自动+1;后者不持有,该对象的内存管理不可控(什么时候释放,跟该指针没关系),不会retain,对象的引用计数不会自动+1;
所以,__strong和__weak修饰的目的是对堆区的内存管理是否管控,而只有对象类型的变量(在堆区创建)才会有管控的问题,基础数据类型变量起始是在栈区存储,其内存(创建/回收)由系统自动管理;
二、__forwarding指针
我们在前面的文章提到,block对int类型的age变量的访问,为什么还要通过__forwarding指针而不是直接访问__Block_byref_age_0结构体中的age变量呢?
分析:
<1>__forwarding指针本身是指向__Block_byref_age_0结构体本身;第一个age又是__Block_byref_age_0结构体类型的指针;第二个age是__Block_byref_age_0结构体中int型成员变量;
<2>在栈区:age->__forwarding->age <=> age->age 没有任何问题;但是在堆区:因为block对象结构体会被copy到堆区,而原先留在栈区的block中的__forwarding指针会自动指向堆区的__Block_byref_age_0结构体;
<3>从上述分析,我们很清楚地知道,将__Block_byref_age_0结构体一并copy到堆区的目的就是堆区的block对象强引用该结构体,所以指向堆区的block对象的各类指针(包括对象本身)都可以通过该block对象达到对__Block_byref_age_0结构体中age变量值的改变等操作的目的,而不必担心int型age变量内存随时会被系统回收的风险
————问题来了:如果栈区的指针或者block对象本身要对age变量的值进行修改,是要面临该风险的,那如何规避呢?
就是通过__forwarding指针,因为此时栈区的_Block_byref_age_0结构体中的__forwarding指针变量是指向堆区的_Block_byref_age_0结构体,除非堆区_Block_byref_age_0结构体内存被手动销毁,否则会一直存在;
//图解
三、__block修饰对象类型变量
//代码
void test2()
{
Person *per = [[Person alloc] init];
per.age = 20;
__block Person *blockPer = per;
void(^block)(void) = ^{
blockPer.age = 30;
NSLog(@"%d", blockPer.age);
};
block();
}
//打印
2019-01-17 11:17:12.020409+0800 MJ_TEST[1304:76856] 30
2019-01-17 11:17:12.020869+0800 MJ_TEST[1304:76856] -[Person dealloc]
Program ended with exit code: 0
//clang
struct __Block_byref_blockPer_1 {
void *__isa;
__Block_byref_blockPer_1 *__forwarding;
int __flags;
int __size;
void (*__Block_byref_id_object_copy)(void*, void*);
void (*__Block_byref_id_object_dispose)(void*);
Person *blockPer;
};
struct __test2_block_impl_0 {
struct __block_impl impl;
struct __test2_block_desc_0* Desc;
__Block_byref_blockPer_1 *blockPer; // by ref
__test2_block_impl_0(void *fp, struct __test2_block_desc_0 *desc, __Block_byref_blockPer_1 *_blockPer, int flags=0) : blockPer(_blockPer->__forwarding) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
static void __test2_block_copy_0(struct __test2_block_impl_0*dst, struct __test2_block_impl_0*src) {_Block_object_assign((void*)&am