MT iOS

Swift之[weak self] 和 [unowned sel

2022-06-29  本文已影响0人  小羊爱学习

[weak self] 表示 self为可选型 可以为nil 所以在使用的时候必须解包
[unowned self]由于在使用前要保证一定有这个对象 所以不必解包
如果捕获(比如 self)可以被设置为 nil,也就是说它可能在闭包前被销毁,那么就要将捕获定义为 weak。
如果它们一直是相互引用,即同时销毁的,那么就可以将捕获定义为 unowned。
推荐使用weak self

loadData = { [weak self] (value) in//value 是闭包参数,没有就不写
     print(self.xxx)
 }

eg1:使用self?解包用法

        logVC.dissmissCallback = { [weak self] in
            self?.logWindow?.alpha = 1
        }

eg2:使用guard let self = self else { return }

logWindow?.tapCallback = { [weak self] in
            guard let self = self else { return }
            self.logWindow?.alpha = 0
        }

eg3:闭包套闭包用法:即使第一个闭包内没有用到self也要写[weak self],因为编译器的行为,为了能够让内层的闭包弱引用 self,外层的闭包默认强引用了 self,也就是在第一个外层闭包里摸人强引用了self。这里注意第二个闭包,其实如果在第一个闭包里面没有使用guard let self = self else { return } 让给self重新定义,这里第二个闭包里面的[weak self] in是可以不用写的。比如看eg4

            retrier.should(sessionManager, retry: request, with: error) { [weak self] shouldRetry, timeDelay in//注意此处第一个闭包内并没有用到self,但也是要写的。

                guard shouldRetry else { completeTask(session, task, error) ; return }
//self?.sessionManager 如果此处用到self,因为没有guard let self = self else { return },所以必须使用self?
                DispatchQueue.utility.after(timeDelay) { [weak self] in
                    guard let strongSelf = self else { return }

                    let retrySucceeded = strongSelf.sessionManager?.retry(request) ?? false

                    if retrySucceeded, let task = request.task {
                        strongSelf[task] = request
                        return
                    } else {
                        completeTask(session, task, error)
                    }
                }
            }

eg4:这里我们先不考虑使用self?解包,在第一个闭包dataTask内,我们并没有使用guard let self = self else { return }给self重定义,因此在第二个闭包async内部我们可以不用在去[weak self] in去弱化,此时第二个闭包里面用的就是第一个闭包中已的self,本身已经是弱引用了。 然而第三个闭包animate时,因为第二个闭包已经给self重定义,因此第三个闭包再次使用self时,必须再次弱引用上层闭包的self。

    URLSession.shared.dataTask(with: URL(string: "www.jsc.png")!) {[weak self] (data, res, err) in
        let image = UIImage(data: data!)
//            self?.imageView?.alpha = 0.5
        
        DispatchQueue.main.async {//因为上一层并没有使用guard let self = self else { return }给self重新定义,一次这里可以不用再写[weak self] in
            guard let self = self else { return }
            self.imageView?.image = image
            
            UIView.animate(withDuration: 1){ [weak self] in//因此上层闭包使用了guard let self = self else { return },因此这里必须写[weak self] in
                guard let self = self else { return }
                self.imageView?.alpha = 1
                
                //这样也是可以,有点类似oc写法
                //guard let strongSelf = self else { return }
                //strongSelf.imageView.alpha = 1
            }
        }
    }

eg5:如果不用guard let self = self else{ return },则需要self?解包,只需要在在外层闭包声明[weak self]即可

        URLSession.shared.dataTask(with: URL(string: "www.jsc.png")!) {[weak self] (data, res, err) in
            let image = UIImage(data: data!)
            DispatchQueue.main.async {
                self?.imageView.image = image
                UIView.animate(withDuration: 1){
                    self?.imageView.alpha = 1
                }
            }
        }

ps:注意,以上例子只是为了加深探索guard let self = self else{ return }而写,并不是真的全部都正确,比如例子中的animate,其实不需要弱引用,想知道为什么接着往下看吧

扩展:
循环引用:当两个不同的对象各有一个强引用指向对方时就造成循环引用,会导致对象无法释放,就会造成循环引用,永远无法释放。比如我们常见的oc中的block和swift中的闭包
并不是所有的block和闭包都会造成循环引用,以oc中的block为例

block()//block并没有进行被self强引用,因此在block内部实现时也不需要对self进行弱引用
self.block = block//self对block进行了强引用,在block内部使用到self时就需要对self进行弱引用了
想一些个block一般都会作为self的属性被强指针指向,因此大多数会造成循环引用,所以大多数的block都是会在内部实现self弱化

不会造成循环引用的一些block(swift中叫做闭包)
1.block作为Cocoa API中方法名含有usingBlock的方法参数时

//eg:系统帮我们实现的一些block
[UIView animateWithDuration:0.2 animations:^{
    self.alpha = 1;
}];

2.block作为GCD API的方法参数时
注意:如果控制器持有block的话,是会造成循环引用,如果我们只持有queue是不会造成循环引用。

dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
    NSLog(@"不会产生循环引用");
})

3.类方法
类似于第一种,自定义类方法,block作为方法参数时,也不会造成循环引用,因为self无法对一个类强引用。

  1. masonry、AF等三方库(具体可以进三方库的实现里面看一下)
- (NSArray *)mas_makeConstraints:(void(^)(MASConstraintMaker *))block {
    self.translatesAutoresizingMaskIntoConstraints = NO;
    MASConstraintMaker *constraintMaker = [[MASConstraintMaker alloc] initWithView:self];
    block(constraintMaker);
    return [constraintMaker install];
}

block并没有被view引用,block执行完毕就会释放,不会造成循环引用。
5.局部变量
局部变量的block没有被其他对象强引用的时候,在当前作用域结束就会销毁。

但并不是block里都需要用weak,只有该对象持有block,而block里的代码块又持有该对象时才需要用到weak。

上一篇下一篇

猜你喜欢

热点阅读