on alloc] init]实例对象
补充:所以block循环引用造成的直接后果是内存泄露(即程序结束而内存没有被回收——>根本原因是对象引用计数大于0(retain和release使用次数不对等)——>是因为强指针引用造成的);
引伸:当对象所占内存被回收时,指向对象的指针(强指针)应当被赋值于nil或者指向其他的合法内存,否则会导致野指针调用(乱指)程序崩溃——但是,用weak做内存管理策略(即修饰指针变量)时,为什么系统会自动将指针变量置为nil?这点后面文章会提到!
二、解决方案
思路:
据上分析,打破循环引用,只需要将其中一个强引用变成弱引用即可,那么要改变哪一个弱引用呢?Person实例对象内部拥有block属性,当该实例对象销毁时,其block属性也会随之销毁,所以我们只需要将block对象中的Person类型指针变成弱指针即可——通常都是这样做!
//图解
1)ARC环境下
方案一:weak修饰
//代码
void test3()
{
Person *per = [[Person alloc] init];
per.age = 10;
__weak Person *weakPer = per;
per.block = ^{
NSLog(@"-------%d", weakPer.age);
};
}
//打印
2019-01-18 14:10:17.451718+0800 MJ_TEST[1458:103419] -[Person dealloc]
2019-01-18 14:10:17.452663+0800 MJ_TEST[1458:103419] ----
Program ended with exit code: 0
//clang
struct __test3_block_impl_0 {
struct __block_impl impl;
struct __test3_block_desc_0* Desc;
Person *__weak weakPer;
__test3_block_impl_0(void *fp, struct __test3_block_desc_0 *desc, Person *__weak _weakPer, int flags=0) : weakPer(_weakPer) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
分析:
<1>block对象中,Person指针变量类型变成了__weak类型,打印前person对象销毁了;
<2>另外一种写法:__weak typeof(per) weakPer = per <=> __weak Person *weakPer = per;(前者写法居多)
方案二:__unsafe_unretained修饰
注:__weak修饰和__unsafe_unretained,二者有一个非常重要的区别:
经上分析,我们知道Person实例对象销毁后,其内部的block属性也会销毁,那么其也就不再指向block对象了,而此时一旦block对象没有任何强引用,作用域结束后,其也会被销毁,其成员变量Person指针也会被销毁,这点没问题!————但是,如果block对象还存在呢(被其他指针强引用),此时其内部成员变量Person指针也存在,但是依然会指向Person实例对象销毁前所占的内存区域,但是该内存区域已经被系统回收了,Person指针指向的是不合法的内存区域——如果是weak修饰,系统会自动将指针置为nil(指向合法的内存区域);如果是__unsafe_unretained修饰,什么也不会做,这样就会导致野指针调用!
方案三:__block修饰
//代码
void test4()
{
__block Person *per = [[Person alloc] init];
per.age = 10;
per.block = ^{
NSLog(@"-------%d", per.age);
per = nil;
};
per.block();
}
//打印
2019-01-18 15:20:06.697898+0800 MJ_TEST[1810:136831] -------10
2019-01-18 15:20:06.698250+0800 MJ_TEST[1810:136831] -[Person dealloc]
2019-01-18 15:20:06.698300+0800 MJ_TEST[1810:136831] ----
Program ended with exit code: 0
//clang
struct __Block_byref_per_0 {
void *__isa;
__Block_byref_per_0 *__forwarding;
int __flags;
int __size;
void (*__Block_byref_id_object_copy)(void*, void*);
void (*__Block_byref_id_object_dispose)(void*);
Person *__strong per;
};
struct __test4_block_impl_0 {
struct __block_impl impl;
struct __test4_block_desc_0* Desc;
__Block_byref_per_0 *per; // by ref
__test4_block_impl_0(void *fp, struct __test4_block_desc_0 *desc, __Block_byref_per_0 *_per, int flags=0) : per(_per->__forwarding) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
分析:
<1>block对象持有__block对象,而__block对象又持有Person实例对象,而Person实例对象又持有block对象——如此,构成一个三角循环:
<2>通过block回调,Person实例对象指针被置为nil,而该指针本质是__block对象中的Person *__strong per指针,因此该指针不可能再指向Person实例对象了,所以,第2条持有就断开了,打破了三角循环;
说明:但是该方案看起来比较麻烦,一旦忘记将指针置为nil,就会造成内存泄露;
注:以上分析不理解,请参考前述block文章,此处不再赘述!
2)MRC环境
说明:该环境下不支持__weak修饰;
方案一:__un