设为首页 加入收藏

TOP

block本质探寻八之循环引用(二)
2019-08-26 07:00:41 】 浏览:69
Tags:block 本质 探寻 循环 引用
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

首页 上一页 1 2 3 下一页 尾页 2/3/3
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇Swift构造 下一篇笔记:iOS字符串的各种用法(字符..

最新文章

热门文章

Hot 文章

Python

C 语言

C++基础

大数据基础

linux编程基础

C/C++面试题目