设为首页 加入收藏

TOP

block本质探寻八之循环引用(三)
2019-08-26 07:00:41 】 浏览:68
Tags:block 本质 探寻 循环 引用
safe_unretained修饰

//代码

void test5()
{
//    __unsafe_unretained Person *per = [[Person alloc] init];
    __block Person *per = [[Person alloc] init];
    per.age = 10;
    per.block = [^{
        NSLog(@"-------%d", per.age);
    } copy];
    [per release];
    per = nil;
}

//打印

2019-01-18 16:42:49.970441+0800 MJ_TEST[2257:177470] -[Person dealloc]
2019-01-18 16:42:49.971587+0800 MJ_TEST[2257:177470] ----
Program ended with exit code: 0

分析:

<1>根据习惯,MRC环境下,我们通常会将block对象(等号右边)从栈区copy到堆区,以达到手动控制其内存销毁的目的;

<2>原理:调用alloc创建对象时,系统会自动将该实例对象引用计数置为1,而该对象又会随着block的copy而一起被copy到堆区,此时该对象的retainCount会加1(变成2),当对该对象发送release消息时,其retainCount自动减1(由2变成1),所以当程序结束时,Person实例对象retainCount为1(>0),其内存并不会被系统回收从而导致内存泄露;

那么,__unsafe_unretained修饰后,无论后面有多少次retain或者copy操作,Person实例对象的retainCount始终为1,所以程序结束前release时,其retainCount值变为0,此时内存被回收,而不会导致内存泄露的问题;

 

方案二:__block修饰 

 

 分析:为什么block可以?

<1>前述文章我们分析到,MRC环境下,__block修饰对象类型的auto局部变量,系统生成的__block对象并不会根据其内存成员变量Person指针变量(其实就是test5()方法中的per指针)是强指针类型而对Person实例对象([[Person alloc] init])进行retain操作(强引用);

<2>所以此时,__block的作用相当于__unsafe_unretained的作用,原理一样;

 

补充一个问题:在ARC环境下,弱指针不能通过"->"形式来访问对象的成员变量

原因:就是weakSelf很可能为为空(即有可能提前被释放了),所以必须使用强指针来访问

//代码

Person.m

- (void) test6
{
    __weak typeof(self) weakSelf = self;
    self.block = ^{
        __strong typeof(weakSelf) strongSelf = weakSelf;
        NSLog(@"-------%d", strongSelf->_age);
    };
}

main.m

int main(int argc, const char * argv[]) {
    @autoreleasepool {
//        test1();
//        test2();
//        test3();
//        test4();
//        test5();
        Person *per = [[Person alloc] init];
        [per test6];
    }
    
    NSLog(@"----");
    return 0;
}

//打印

2019-01-18 17:43:41.010848+0800 MJ_TEST[2557:208583] -[Person dealloc]
2019-01-18 17:43:41.011346+0800 MJ_TEST[2557:208583] ----
Program ended with exit code: 0

//clang

Person.m

struct __Person__test6_block_impl_0 {
  struct __block_impl impl;
  struct __Person__test6_block_desc_0* Desc;
  Person *const __weak weakSelf;
  __Person__test6_block_impl_0(void *fp, struct __Person__test6_block_desc_0 *desc, Person *const __weak _weakSelf, int flags=0) : weakSelf(_weakSelf) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};

分析:block对象的内部成员变量weakSelf依然是weak类型,并不受block代码块内部的__strong转化,该转化只是为了骗取编译器通过编译而已;

 

三、结论

1)原因:block对象与OC对象相互持有(强引用)——OC对象有block属性,block代码块中用到了该实例对象;

2)危害:程序结束时,相互强应用(对象的引用计数>0)导致实例对象所占内存不能及时被系统回收——即内存泄露;

3)解决:

<1>ARC:__weak修饰(常用)、__unsafe_unretained(会引起野指针调用,不推荐)、__block(过于繁琐,不推荐);

<2>MRC:__unsafe_unretained和__block;

 

 

GitHub

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

最新文章

热门文章

Hot 文章

Python

C 语言

C++基础

大数据基础

linux编程基础

C/C++面试题目