- 三种解除闭包循环引用的方式
[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