解决Swift中callback循环引用 - Delegated(Library)

前言

在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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值