美文网首页
Block循环引用

Block循环引用

作者: NapoleonY | 来源:发表于2018-04-14 17:09 被阅读21次

概述

一般在使用Block时,为了防止循环引用,会使用__weak,因此下面主要验证下是否在Block中必须使用__weak

验证

  1. 新建一个类Car
    //Car.h
    #import <Foundation/Foundation.h>
    
    extern NSString *const CarNotificationName;
    typedef void(^SomeBlock)(void);
    
    @interface Car : NSObject
    
    @property (nonatomic, strong) SomeBlock myBlock;
    
    - (void)doWithBlock:(SomeBlock)block;
    - (void)carRunning;
    
    @end
     
    #import "Car.h"
    
    NSString *const CarNotificationName = @"carNotification";
    
    @implementation Car
    
    - (Car *)init {
        self = [super init];
        if (self) {
            [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(receiveNotification) name:CarNotificationName object:nil];
        }
        return self;
    }
    
    - (void)receiveNotification {
        NSLog(@"Receiving Notification");
    }
    
    - (void)carRunning {
    }
    
    - (void)doWithBlock:(SomeBlock)block {
        self.myBlock = block;
        self.myBlock();
    }
    
    - (void)dealloc {
        [[NSNotificationCenter defaultCenter] removeObserver:self];
        NSLog(@"dealloc");
    }
    
    @end
    
    上面代码主要是
    • 新建Car类,只要Car类实例没有被销毁,就会处理CarNotificationName的通知
    • 方法- (void)doWithBlock:(SomeBlock)block;接收一个SomeBlock类型的参数,并被赋值给一个strong类型的属性。
    • 方法- (void)dealloc;注销通知,并打印信息
  2. 现在开始使用Car
    Car *car = [[Car alloc] init];
    [[NSNotificationCenter defaultCenter] postNotificationName:CarNotificationName object:nil];
    
    [car doWithBlock:^{
    
    }];
    
    car = nil;
    
    dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC * 5);
    dispatch_after(time, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        [[NSNotificationCenter defaultCenter] postNotificationName:CarNotificationName object:nil];
    });
    
    • 首先获取Car类实例
    • 紧接着发送CarNotificationName通知,此时Car类实例会收到通知,并在控制台输出信息
    • 然后调用[car doWithBlock:^{}];方法,传入的block中什么不做,然后将Car类实例设为nil,这时Car类实例被销毁了
    • 然后使用 dispatch_after在5秒后发送CarNotificationName通知,此时由于Car类实例被销毁了,因此控制台不会有信息打印出来
  3. 如果此时对上面代码做如下改动
    [car doWithBlock:^{
        [car carRunning];
    }];
    
    发现即使将Car类实例设为nil,5秒后Car类实例依然能够接收CarNotificationName通知,并在控制台打印了对应的信息,也就是Car类实例并没有被销毁
  4. 原因就是循环引用产生
    Car类中对 SomeBlock是一个强引用
     @property (nonatomic, strong) SomeBlock myBlock;
    
    而在步骤3中的改动代码中,在SomeBlock中反过来对Car类实例也进行了强引用,这就构成了一个环,导致它们两个不能被销毁。

解决方案

只要将上述的引用环断开其中的一个就可以了,因此有两种解决方法

  1. 在Block中使用__weak
    Car * __weak weakCar = car;
    [car doWithBlock:^{
        [weakCar carRunning];
    }];
    
  2. Car类中对 SomeBlock弱引用
    @property (nonatomic, weak) SomeBlock myBlock;
    

结论

因此,可以确定不是在所有的Block中都要使用__weak,如果没有构成循环引用,或者对Block是弱引用就可以不必使用__weak
例如GCD或者[UIView animateWithDuration:5.0 animations:^{ }];中没有构成循环引用,就不必使用__weak,而在另一些情况如上述步骤3就需要使用__weak了。

备注

文中测试代码已经上到GitHub

参考

  1. block 循环引用问题的一点发散

相关文章

网友评论

      本文标题:Block循环引用

      本文链接:https://www.haomeiwen.com/subject/kwulkftx.html