Swift之[weak self] 和 [unowned sel
[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无法对一个类强引用。
- 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。