闭包的循环引用

  • 三种解除闭包循环引用的方式
  • [weak self]
    • 表示闭包中的 self 是弱引用,如果 self 被释放,会自动设置为 nil
    • 与 OC 的 __weak 等效
  • [unowned self]
    • 表示闭包中的 self 是 assign 的,如果 self 被释放,指针地址保持不变,会出现野指针的错误
    • 与 OC 的 __unsafe_unretained 等效

创建有循环引用的代码

面试题:请说出工作中遇到的循环引用(不要说没有遇到过)

  • 闭包中使用 self. 对当前对象强引用
  • 控制器(可以接收闭包的任意对象) 以属性记录闭包(强引用)
class ViewController: UIViewController {
    var finishedCallBack: (()->())?
    override func viewDidLoad() {
        super.viewDidLoad()
        loadData {
            // 与 OC 的 block 一样,闭包同样会对外部变量做一次 copy(强引用)
            print("OK \(self)")
        }
    }
    func loadData(completion: ()->()) -> () {
        // 使用属性记录闭包
        finishedCallBack = completion
        DispatchQueue.global().async {
            print("耗时操作")
            Thread.sleep(forTimeInterval: 1.0)
            DispatchQueue.main.async {
                print("主线程回调")
                // 闭包是提前准备好的代码,在执行时需要指定上下文的语境,因此需要 `self.`
                // 使用 `?` 表示一旦闭包不存在就什么都不做
                // 使用 `!` 表示无论闭包是否存在都执行,如果真的不存在,会崩溃
                self.finishedCallBack?()
            }
        }
    }
    // 析构函数 - 类似于 OC 的 dealloc
    deinit {
        print(#function)
    }
}

解除循环引用

方法一
// 1. 方法一,类似于 OC 的方法
// * weak 修饰的变量有可能在运行时被修改为 nil,因此不能使用 let
weak var weakSelf = self
loadData {
    // 与 OC 的 block 一样,闭包同样会对外部变量做一次 copy(强引用)
    print("OK \(weakSelf)")
}
方法二
// 2. 方法二,Swift 的写法
// [weak self] 表示闭包中的 self 是弱引用,如果 self 被释放,会自动设置为 nil
// 与 OC 的 `__weak` 等效
loadData { [weak self] in
    // 与 OC 的 block 一样,闭包同样会对外部变量做一次 copy(强引用)
    print("OK \(self?.view)")
}
方法三 —— 知道就行
// 3. 方法三
// [unowned self] 表示闭包中的 self 是 assign 的,如果 self 被释放,指针地址保持不变
// 会出现野指针的错误
// 与 OC 的 `__unsafe_unretained` 等效
loadData { [unowned self] in
    // 与 OC 的 block 一样,闭包同样会对外部变量做一次 copy(强引用)
    print("OK \(self.view)")
}

OC 循环引用参考代码

@interface DemoViewController ()
@property (weak, nonatomic) IBOutlet UILabel *tipLabel;
@property (nonatomic, copy) void (^completionBlock)();
@end

@implementation DemoViewController

- (void)viewDidLoad {
    [super viewDidLoad];

    // 方法一:使用 __weak 解除循环引用,一旦 self 被释放,block 内部的代码不会被执行
//    __weak typeof(self) weakSelf = self;
//    [self loadDataWithCompletion:^{
//        NSLog(@"over %@", weakSelf.view);
//        weakSelf.tipLabel.text = weakSelf.view.description;
//    }];

    // 方法二:使用 __unsafe_unretained 解除循环引用,一旦 self 被释放,会报野指针错误
    __unsafe_unretained typeof(self) weakSelf = self;
    [self loadDataWithCompletion:^{
        NSLog(@"over %@", weakSelf.view);
        weakSelf.tipLabel.text = weakSelf.view.description;
    }];
}

- (void)loadDataWithCompletion:(void (^)())completion {
    self.completionBlock = completion;

    dispatch_async(dispatch_get_global_queue(0, 0), ^{

        NSLog(@"耗时操作");
        [NSThread sleepForTimeInterval:2.0];

        dispatch_async(dispatch_get_main_queue(), ^{

            NSLog(@"主线程回调");
            //self.completionBlock();
            completion();
        });
    });
}

- (void)dealloc {
    NSLog(@"对象被释放");
}

@end
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值