美文网首页
和闭包中的 [weak self] 说拜拜

和闭包中的 [weak self] 说拜拜

作者: 番茄炒西红柿啊 | 来源:发表于2020-05-21 18:57 被阅读0次

一点题外话

苹果官方的Cocoa API 中大量使用了委托模式.平时开发中最常用的比如tableView, collectionView的代理回调方法.
我们自己封装模块时自然而然的也会经常沿用了这套模式, 以自定的view将内部的textfield内容回调给控制器的情景为例:

// 定义一个协议
protocol TestViewDelegate: class {
    func textInputView(_ view: TestView, didConfirmInput text: String?)
}

class TestView: UIView {
    @IBOutlet weak var inputTextField: UITextField!
    weak var delegate: TestViewDelegate?
    @IBAction func confirmButtonPressed(_ sender: Any) {
        // 委托给代理处理
        self.delegate?.textInputView(self, didConfirmInput: inputTextField.text)
    }
}

class MyViewController : UIViewController {
    var text: String? = ""
    override func viewDidLoad() {
        super.viewDidLoad()
        let testView = TestView.init(frame: CGRect.zero)
        // 设置代理
        testView.delegate = self
    }
}
// 代理回调
extension MyViewController: TestViewDelegate {
    func textInputView(_ view: TestView, didConfirmInput text: String?) {
        self.text = text
    }
}

实现三部曲: 1. 创建协议模板 2.设置代理委托 3.实现回调方法

但是我个人的编程习惯是不太喜欢使用代理委托的方式.因为创建视图设置代理的代码和实现回调的代码是分开的.在维护一段老代码时,我们需要先定位到设置代理的地方,然后点进去看代理遵循的是什么协议, 最后才能定位到代理回调的地方,去查看前任的实现逻辑.

分散的代码,分散的逻辑.碰到一些代码习惯不太友好的前任.维护起来难度比较大.

所以我平常使用block会比委托要多一点

将上述的代码改成block实现:

class TestView: UIView {
    @IBOutlet weak var inputTextField: UITextField!
    var block: ((String?)->Void)?
    @IBAction func confirmButtonPressed(_ sender: Any) {
        self.block?(inputTextField.text)
    }
}
class MyViewController : UIViewController {
    var text: String? = ""
    override func viewDidLoad() {
        super.viewDidLoad()
        let testView = TestView.init(frame: CGRect.zero)
        testView.block = { self.text = $0 }
        self.view.addSubview(testView)
    }
}
// 这样创建视图以及回调的地方都集中在了一起,方便查找和维护.

回到正题

上述代码中有个致命的错误, 就是没有考虑循环引用的问题.所以设置block的代码片段需要修改成:

testView.block = { [weak self] (text) in
    guard let `self` = self else {return}
    self.text = text
 }

都说偷懒是促进人类进步的动力. 每次定义的block都需要手动写上[weak self], 这个Xcode是没有自动补全功能的,实属有些麻烦.而且人难免会犯错,很有可能会出现漏写的情况.

无意中在看喵神博客的时候,发现了前辈是这样处理的:

  1. 定义一个来处理弱引用的类:
class Delegate<Input, Output> {
    typealias blockType = ((Input) -> Output?)?

    // 创建一个私有属性持有block
    private var block: blockType = nil
    
    /// 将需要弱引用的目标对象传递进来,在方法内部处理,并将处理完成后的结果回调出去
    /// - Parameters:
    ///   - target: 需要弱引用的目标对象
    ///   - block: 弱引用处理后的结果回调
    func delegate<T: AnyObject>(on target: T, block: ((T, Input) -> Output)?) {
        self.block = { [weak target] (input) in
            guard let target = target else {return nil}
            return block?(target, input)
        }
    }
    
    /// callAsFunction是固定关键字, 为了可以隐式调用.
    func callAsFunction(_ input: Input) -> Output? {
        return self.block?(input)
    }
}
  1. 在view中写法入下:
class TestView: UIView {
    @IBOutlet weak var inputTextField: UITextField!
    let textDelegate = Delegate<String?, Void>()
    @IBAction func confirmButtonPressed(_ sender: Any) {
        // 隐式调用callAsFunction方法
        textDelegate(inputTextField.text)
    }
}

3 .控制器中的回调代码

let testView = TestView.init(frame: CGRect.zero)
self.view.addSubview(testView)
testView.textDelegate.delegate(on: self) { (self, text) in
    // 这里的已经是弱引用的self
    self.text = text
}

到这里基本就结束了, 通过这种做法就省去了手写 [weak self] 的麻烦.彻底和它说拜拜了.

但是有一点需要注意的是:

testView.textDelegate.delegate(on: self) { ( _ , text) in
    // 将block中回调的第一个参数设置成下划线后
   // 这里的其实是外部的强引用的self, 是会造成内存泄漏的.
    self.text = text
}

参考原文: 链接

相关文章

  • 和闭包中的 [weak self] 说拜拜

    一点题外话 苹果官方的Cocoa API 中大量使用了委托模式.平时开发中最常用的比如tableView, col...

  • Swift 循环引用

    在闭包里面嵌套闭包的时候 [weak self] 必须处于第一个闭包 否则会造成循环引用 例: weak self...

  • Swift中weak与unowned的区别

    在Swift的闭包中为了避免循环引用的问题,通常用[weak self] 或者[unowned self], 前者...

  • Swift 4.2 可以使用 self 做为变量名啦!

    在 4.2 之前,self 是全局保留关键字,所以如果在逃逸闭包中如果在闭包中把 self 标记为 weak 后,...

  • Swift捕获列表Capture List

    闭包的特点 swift的iOS的app中,遍布着各种闭包,闭包中经常出现捕获列表,我们经常用[weak self]...

  • 解决Swift中callback循环引用 - Delegated

    前言 在Swift中我们使用闭包(Callback)的时候经常要写 [weak self] or [unowned...

  • Swift笔记

    Swift中weak与unowned的区别 在闭包里面为了解决循环引用问题,使用了 [unowned self]。...

  • Swift解决循环引用问题

    循环引用一般是存在于block(闭包)里面,是在block(闭包)大括号里面加上[weak self]即可,访问s...

  • Retain Cycle in Swift

    闭包 weak和unowned区别请自己看官方文档。 ‘不正常’的闭包 Swift中是可以将func赋值给闭包的,...

  • Swift 闭包的使用坑

    很多人在使用闭包的时候容易引起循环引用,解决循环引用的问题可以使用 [unowned self] 和 weak v...

网友评论

      本文标题:和闭包中的 [weak self] 说拜拜

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