p;dst->blockPer, (void*)src->blockPer, 8/*BLOCK_FIELD_IS_BYREF*/);}
static void __test2_block_dispose_0(struct __test2_block_impl_0*src) {_Block_object_dispose((void*)src->blockPer, 8/*BLOCK_FIELD_IS_BYREF*/);}
static struct __test2_block_desc_0 {
size_t reserved;
size_t Block_size;
void (*copy)(struct __test2_block_impl_0*, struct __test2_block_impl_0*);
void (*dispose)(struct __test2_block_impl_0*);
}
分析:
<1>我们发现,__block修饰的blockPerl实例对象,系统也会自动生成一个新的对象__Block_byref_blockPer_1;被引用的类对象以指针的形式存在于该结构体中(Person *blockPer),该指针指向[[Person alloc] init]这个实例对象(位于堆区);
<2>在__Block_byref_blockPer_1结构体中,还存在__Block_byref_id_object_copy和__Block_byref_id_object_dispose两个函数指针,分别指向__Block_byref_id_object_copy_131函数和__Block_byref_id_object_dispose_131函数(作用同上),如下:
static void __Block_byref_id_object_copy_131(void *dst, void *src) {
_Block_object_assign((char*)dst + 40, *(void * *) ((char*)src + 40), 131);
}
static void __Block_byref_id_object_dispose_131(void *src) {
_Block_object_dispose(*(void * *) ((char*)src + 40), 131);
}
很显然,这两个函数的作用也是针对某个对象的内存管理,那是哪个对象呢?
首先,_Block_object_assign和_Block_object_dispose的第三个参数为131,是指__block修饰实例对象的情形;
其次,dst是__Block_byref_blockPer_1对象的地址,加40是什么?我们算出Person *blockPer指针的地址偏移量正好为40(8+8+4+4+8+8,指针变量占8个字节),那么可以肯定,上述两个函数就是对Person *blockPer指向的实例对象[[Person alloc] init]的内存管理;
所以,我们可以推测出以下结构:block对象__test2_block_impl_0通过其内部成员变量blockPer持有__Block_byref_blockPer_1对象,而__Block_byref_blockPer_1对象又通过其内部成员变量blockPer持有[[Person alloc] init]实例对象;
我们知道,前者通过_test2_block_copy_0函数和__test2_block_dispose_0函数进行内存管理,其持有必定是强引用,,这点没问题;而后者的持有是通过__Block_byref_id_object_copy_131函数和__Block_byref_id_object_dispose_131函数进行内存管理,但其持有是强引用还是弱引用呢?往下看;
//__weak修饰
__block __weak Person *blockPer = per;
//clang:xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc -fobjc-arc -fobjc-runtime=ios-9.0.0 main.m(注:要设置runtime,否则编译会报错)
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 *__weak blockPer;
};
//打印
2019-01-17 12:17:20.776779+0800 MJ_TEST[1598:105742] 30
2019-01-17 12:17:20.777113+0800 MJ_TEST[1598:105742] -[Person dealloc]
Program ended with exit code: 0
分析:
<1>我们发现,如果没有__weak修饰,blockPer格式为Person *blockPer,默认为strong类型;__weak修饰后,则会变成weak类型;
<2>根据之前的分析,其实__Block_byref_blockPer_1对象对[[Person alloc] init]实例对象的引用,取决于指向该实例对象的指针类型(因为对象引用是指针传递,前面已讲过);
------!!!但是,该规则仅限于ARC模式的情形,MRC模式下,如果指针是strong类型,系统并不会执行retain操作!!!
这里有个问题,为什么__weak修饰后,Person实例对象打印前没有被销毁呢?因为该实例对象的作用域在test2()函数体内,而block的回调也在函数体内,因此回调时,该实例对象并没有被销毁;
接下来,我们可以验证下:
分析:此时,block回调前,Person实例对象就被销毁了,说明block对象对实例对象的引用取决于Person对象指针的引用类型;
我们再切换到MRC模式下看看:
//代码
void test4()
{
Person *per = [[Person alloc] init];
MyBlock block = [^{
NSLog(@"%p", per);
} copy];
[per release];
block();
[block release];
}
//打印
2019-01-17 13:52:05.667590+0800 MJ_TEST[2106:151409] 0x10061add0
2019-01-17 13:52:05.668200+0800 MJ_TEST[2106:151409] -[Person dealloc]
Program ended with exit code: 0
分