首页 > 解决方案 > 使包装器将自我类型化为自我的扩展

问题描述

对不起,奇怪的标题。这是我的代码的玩具草图:

extension UIControl {
    func makeHolder() -> ControlHolder {
        ControlHolder(control: self)
    }
}

struct ControlHolder {
    let control : UIControl
    init(control: UIControl) {
        self.control = control
    }
    func retrieve() -> UIControl {
        return self.control
    }
}

我承认这是一个玩具还原,我不喜欢人们这样做,但它完美地说明了句法问题,所以让我们继续吧。

好的,所以我们在 UIControl 上有一个扩展,它是一个返回包装器对象的方法。现在,问题是这样的:

let holder = UISwitch().makeHolder()
let output = holder.retrieve()

结果output, 显然是作为 UIControl 输入的。但这不是我想要的。我希望它输入为 UISwitch,因为我从 UISwitch 开始。好的,所以这听起来像一个通用的。问题是,我不知道如何使它通用。

我认为,使 ControlHolder 成为泛型很容易:

struct ControlHolder<T:UIControl> {
    let control : T
    init(control: T) {
        self.control = control
    }
    func retrieve() -> T {
        return self.control
    }
}

我很确定我的那部分是正确的。但是,我该如何编写扩展声明,以便将泛型解析为实际类型,即调用selfUIControl的类型?makeHolder

我尝试在扩展中引入泛型,服从编译器,直到我编译它:

extension UIControl {
    func makeHolder<T>() -> ControlHolder<T> {
        ControlHolder<T>(control: self as! T)
    }
}

但这很愚蠢,并且output仍然输入为 UIControl。

显然,我可以添加另一个参数,将类型显式传递给makeHolder并解决它:

extension UIControl {
    func makeHolder<T>(ofType: T.Type) -> ControlHolder<T> {
        ControlHolder<T>(control: self as! T)
    }
}

现在,当我打电话时,makeHolder我传入了类型:

let holder = UISwitch().makeHolder(ofType: UISwitch.self)
let output = holder.retrieve()

现在当然output是输入为 UISwitch。但这是愚蠢的!我希望扩展只知道类型是 UISwitch,因为我正在调用makeHolderUISwitch。

我觉得我在这一切都错了。也许有人可以纠正我?还是我的目标是不可能的事情?

标签: iosswiftgenerics

解决方案


这样做的诀窍是定义一个协议,该协议的扩展,并将该makeHolder方法放入该扩展中。这样,您可以将Self其用作返回的泛型类型ControlHolder

首先定义一个新协议(我们称之为“ HoldableControl”)并要求conformers必须是UIControls。它不需要任何其他要求,因为我们只关心将makeHolder功能添加到扩展中。

protocol HoldableControl: UIControl {}

然后,在其中添加一个扩展名HoldableControl和定义makeHolder,返回ControlHolder<Self>. 我们可以在Self这里使用,因为它在协议扩展中是允许的,不像在UIControl.

extension HoldableControl {
    func makeHolder() -> ControlHolder<Self> {
        ControlHolder(control: self)
    }
}

然后,我们只需要UIControl符合这个协议:

extension UIControl: HoldableControl {}

ControlHolder并像您已经完成的那样使您的通用:

struct ControlHolder<T: UIControl> {
    let control: T
    
    init(control: T) {
        self.control = control
    }
    
    func retrieve() -> T {
        control
    }
}

现在它将起作用:

let holder = UISwitch().makeHolder() // type is ControlHolder<UISwitch>
let output = holder.retrieve()       // type is UISwitch

推荐阅读