美文网首页
解决Swift中callback循环引用 - Delegated

解决Swift中callback循环引用 - Delegated

作者: 普通上班族老王 | 来源:发表于2019-02-15 17:16 被阅读0次

    前言

    在Swift中我们使用闭包(Callback)的时候经常要写 [weak self] or [unowned self].
    而且你不写, 系统也不会提示你, 这就很容易出现循环引用. ( /(ㄒoㄒ)/~~ 表示本人已经忘记无数次了. )
    所以为了避免这种情况, 自然而然就有了这个库 Delegated .

    使用示例

    一般闭包示例

    // 声明
    typealias XQCallback = (String) -> ()
    private var callback: XQCallback?
    // 调用闭包
    callback(str)
    
    // 外部给闭包赋值
    self.callback { [unowned self] (value) in
        print(self, value)
    }
    

    Delegated 示例

    // 声明, 并创建结构体  <inputValue, outputValue>
    // inputValue: 调用闭包传出去的值
    // outputValue: 闭包内return出来的值
    var callback = Delegated<String, Void>.init()
    // 调用闭包
    callback.call("inputValue")
    
    // 外部给闭包赋值
    callback.delegate(to: self) { (self, inputValue) in
        print(self, inputValue)
    }
    

    使用起来, 和原来的闭包写的差不多, 但是这边是把 self 作为参数传进去, 避免了忘写的尴尬.

    源码解析

    整个库就一个文件, 而且代码很少, 我们一个一个方法来解析.

    public struct Delegated<Input, Output> {
        // 赋值闭包, 无需自己写weak
        // !!!这个是主要方法, 看明白这个就可以了
        public mutating func delegate<Target : AnyObject>(to target: Target,
                                                          with callback: @escaping (Target, Input) -> Output) {
            
            // 赋值callback, 并且weak传入的target值 (这里就把原本我们要写的weak做了)
            self.callback = { [weak target] input in
                guard let target = target else {
                    return nil
                }
                return callback(target, input)
            }
        }
        
        // 调用callback
        public func call(_ input: Input) -> Output? {
            return self.callback?(input)
        }
        // 声明闭包
        private(set) var callback: ((Input) -> Output?)?
        // 是否已经赋值callback true已赋值, false未赋值
        public var isDelegateSet: Bool {
            return callback != nil
        }
        public init() { }
    
    }
    
    // 这个扩展用到了一个 where Output == Void, 很巧妙.
    // 这样的话, 传入output是Void类型, 调用闭包时, 就会自动为无返回值.
    extension Delegated where Output == Void {
        // 调用闭包
        public func call(_ input: Input) {
            self.callback?(input)
        }
    }
    
    // 这个扩展不需要看太多, 我大致标明一下函数含义就行
    extension Delegated {
        // 赋值闭包, 需要自己写weak
        public mutating func stronglyDelegate<Target : AnyObject>(to target: Target,
                                                                  with callback: @escaping (Target, Input) -> Output) {}
        // 赋值闭包, 需要自己写weak
        public mutating func manuallyDelegate(with callback: @escaping (Input) -> Output) {}
        // 删除当前已赋值的闭包
        public mutating func removeDelegate() {}
    }
    

    虽然代码量少, 但是这个思路真的不错, 胜在实用性.
    不过这个第三方还有几个点没解决

    • 没提供能动态多个weakTarget
    • input只有一个参数.

    不过这都是小事,明白原理之后, weakTarget可以自己照着写, 多写几个就行.(应该也不会用很多个吧 ✧(≖ ◡ ≖) )
    而参数问题, 我们可以不写多几个函数, 我们可以直接用元组, 如下:

        // typealias 一个元组类型
        typealias Login = (acc: String, pwd: String)
        // Delegated 声明并初始化
        var callback = Delegated<Login, Void>.init()
        // 点击登录按钮
        @IBAction func respondsToBtn(_ sender: Any) {
            // Delegated 调用闭包
            self.callback.call(("账号", "密码"))
        }
    
        // 外部赋值闭包
        self.tView.callback.delegate(to: self) { (self, info) in
            print("acc:", info.acc, "\npwd:", info.pwd)
        }
    

    这样一个参数也无所谓了

    示例Demo

    写了一个使用示例demo, 两个页面的跳转, 按钮的点击是一个view传到viewController.
    展示了普通闭包和使用Delegated的区别


    在这里插入图片描述

    使用普通闭包回跳.
    真的容易忘写 /(ㄒoㄒ)/~~ ,自从写Swift以来, 不知道忘写多少次了.


    在这里插入图片描述

    使用Delegated回跳


    在这里插入图片描述

    示例Demo地址

    DelegatedDemo

    相关文章

      网友评论

          本文标题:解决Swift中callback循环引用 - Delegated

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