iOS开发——Block引起循环引用的解决方案

Posted 乞力马扎罗的雪CYF

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了iOS开发——Block引起循环引用的解决方案相关的知识,希望对你有一定的参考价值。

       内存问题始终是软件开发中的头等大事,ios开发中也不例外,在面试中也是必问的问题。今天我们主要来讲讲Block中涉及的循环引用问题。当我们自己一开始写代码的时候,可能会大量在block中使用self,但是当看到别人优秀的代码的时候,发现别人常常不是用self,而使用weakSelf. 为什么呢?本文的示例代码上传至 https://github.com/chenyufeng1991/Block_WeakSelf 。 

       首先我先来说说内存管理的原则:

1.默认使用strong,可选weak。strong下不管成员变量还是属性,每次使用指针指向一个对象,就会自动调用retain,并对旧对象调用release,在需要释放的时候设为nil。

2.避免循环引用,否则手动设置nil释放。

3.创建block匿名函数之前一般需要对self进行weak化,否则造成循环引用无法释放controller。

      首先Xcode为我们提供了良好的编译环境,如果代码中有可能出现循环引用的地方,Xcode会给我们警告:“Capturing ‘self‘ strongly in this block is likely to lead to a retain cycle”.如图:

技术分享


      block中的循环引用是这样的:某个对象有一个copy或者strong成员变量或者属性,这时block内部直接引用了成员变量或者self,这样就产生了self持有block成员,block成员持有self,就会导致循环引用。因为self本身就是一个strong类型的变量。苹果官方的建议是:传进block之前,把self转换成weak automatic的变量,这样在block中就不会出现对self的强引用。如果在block执行完成之前,self被释放,weakSelf也会置为nil。weak类型相对比较安全,因为可以在释放后自动置为nil,不会引起野指针。那么如何来声明呢?

 1.

__weak typeof(self) weakSelf = self;

这句话的意思是声明了一个self类型的weak指针,名字叫做weakSelf.  typeof是用来求参数类型的,这里也就是来求self的类型。这样定义出的weakSelf就是和self是一个类型,并且是原self的一个弱引用。


2.

__weak typeof(&*self) weakSelf = self;

3.

__weak MyViewController *weakSelf = self;


下面我通过代码演示一下:

(1)声明几个block和一个属性:

@interface ViewController (){

    void(^myBlock1)(void);//该block参数为void,返回值为void
    void(^myBlock2)(void);
    void(^myBlock3)(void);
}

@property (nonatomic,copy) NSString *person;

@end

(2)使用weakSelf不会引起循环引用:

    __weak typeof(self) weakSelf = self;

    NSLog(@"init--> value:%@,address=%p,self=%p",self.person,self.person,self);

    myBlock1 = ^(void){
        //这样不会造成循环引用
        NSLog(@"execute1--> value:%@,address=%p,weakSelf=%p",weakSelf.person,weakSelf.person,weakSelf);
    };


(3)直接使用self,会循环引用:Xcode会给警告

    myBlock2 = ^(void){
        //这样造成循环引用
        NSLog(@"execute2--> value:%@,address=%p,self=%p",self.person,self.person,self);
    };


(4)要执行的方法抽取出来,也不会循环引用:

    myBlock3 = ^(void){
        //这样也不会造成循环引用,已经抽取出要执行的方法
        [weakSelf myBlock3Func];
    };

- (void)myBlock3Func{

    NSLog(@"execute3--> value:%@,address=%p,self=%p",self.person,self.person,self);
}


(5)block不是self的属性或者变量时,在block内使用self也不会循环引用:

    //block不是self的属性时,block内部使用self也不是循环引用
    Animal *animal = [[Animal alloc] init];
    animal.animalBlock = ^(void){

        NSLog(@"animal--> value:%@,address=%p,self=%p",self.person,self.person,self);
    };


(6)block的调用如下:

    myBlock1();
    myBlock2();
    myBlock3();

    animal.animalBlock();    


以上是关于iOS开发——Block引起循环引用的解决方案的主要内容,如果未能解决你的问题,请参考以下文章

block-循环引用

iOS中block循环引用问题

iOS Block循环引用精讲

iOS底层探索之Block——如何解决Block循环引用问题?

iOS 循环引用解决方案

iOS开发笔记15:地图坐标转换那些事block引用循环UICollectionviewLayout及瀑布流图层混合