【问题标题】:Add a generic delegate to a base class in Swift将泛型委托添加到 Swift 中的基类
【发布时间】:2021-01-15 11:53:16
【问题描述】:

理想情况下,我想创建一个BaseViewController 类,该类接受(委托的)协议类型并有一个弱变量作为委托。像这样的:

class BaseViewController<Delegate: AnyObject> {
    weak var delegate: Delegate?

    init(delegate: Delegate) {
        self.delegate = delegate
        super.init(...)
    }
}

然后像这样从视图控制器继承:

protocol MyDelegate: AnyObject { 
    func funcA()
    func funcB()
}  

class SomeViewController: BaseViewController<MyDelegate> {
    func doSomething() {
        delegate?.funcA()
    }
}

编译器抱怨这不起作用:

'BaseViewController' 要求 'MyDelegate' 是类类型

如何解决这个问题以实现我的需要?
在此先感谢:)

【问题讨论】:

  • 那是因为在 swift 协议中不会自己确认,您不能使用“MyProtocol”作为确认协议“MyDelegate”示例的具体类型:stackoverflow.com/questions/33112559/…
  • 协议不是对象。您需要创建一个符合MyDelegate 协议的类,例如MyDelegateClass,并使用它来专门化您的泛型。

标签: ios swift generics delegates protocols


【解决方案1】:

那是因为在 swift 协议中不会向它们自己确认,您不能使用“MyProtocol”作为确认协议“MyDelegate”的具体类型

你宁愿做的是

protocol MyDelegate: AnyObject {
    func funcA()
    func funcB()
}

class BaseViewController<Delegate: MyDelegate> {
    weak var delegate: Delegate?

    init(delegate: Delegate) {
        self.delegate = delegate
        super.init(...)
        //keeping OPs code as is
    }
}

class SomeOtherDelegateClass: MyDelegate {
    func funcA() {
        //some code here
    }

    func funcB() {
        //some code here
    }


}

class SomeViewController: BaseViewController<SomeOtherDelegateClass> {
    func doSomething() {
        self.delegate?.funcA()
    }
}

编辑 1:

正如评论中提到的 OP,他试图在 BaseViewController 中引入一个泛型属性,该属性将简单地持有对任何实例的弱引用,该实例的类由 BaseViewController 的子类使用泛型决定/声明,我正在简化上面回答了一下

试试这个

protocol MyDelegate {
    func funcA()
    func funcB()
}

class BaseViewController<Delegate> where Delegate: AnyObject {
    weak var delegate: Delegate?

    init(delegate: Delegate) {
        self.delegate = delegate
        super.init(...)
        //keeping OPs code as is
    }
}

class SomeOtherDelegateClass: MyDelegate {
    func funcA() {
        //some code here
    }

    func funcB() {
        //some code here
    }
}

class SomeViewController: BaseViewController<SomeOtherDelegateClass> {
    func doSomething() {
        self.delegate?.funcA()
    }
}

protocol MyDelegate2 {
    func funcABCD()
}

class SomeOtherDelegateClass2: MyDelegate2 {
    func funcABCD() {
        //some code here
    }
}


class SomeViewController2: BaseViewController<SomeOtherDelegateClass2> {
    func doSomething() {
        self.delegate?.funcABCD()
    }
}

TBH,我真的看不出这种设计有什么好处!可能你需要重新审视代码结构,看看你是否能想出更好的代码结构:)

【讨论】:

  • 问题是我需要委托类型是通用的,并由继承自BaseViewController 的类决定,所以我不能让BaseViewController 将其委托类型绑定到MyDelegate
  • @eilon:正如我在回答中已经提到的那样,swift 不允许协议确认自身,因此您不能将协议作为通用参数传递 BaseViewController&lt; MyDelegate&gt; 是不允许的。所以你需要重新考虑你的代码结构,很有可能你可以重新设计你的代码以更好地构建它并避免这样的陷阱。
  • @eilon:检查编辑,看看是否有帮助
  • @eilon:第二次编辑应该能满足您的要求 :) 请立即查看
  • 谢谢 Sandeep,为泛型提供采用类确实有效,尽管这并不理想,因为现在我的 ViewController 可以访问整个类接口而不仅仅是协议函数。无论如何,我会尝试考虑更好的实现。我正在尝试以这种方式设计它,因为此视图控制器模式在我的应用程序中多次重复出现,我想尝试将样板代码缩减为超类。
【解决方案2】:

您应该将您的委托设置为 BaseViewController 中泛型类型 T 的约束:

protocol MyDelegate: AnyObject {
    func funcA()
    func funcB()
}

class Delegated1: MyDelegate {
    func funcA() { print("A1") }
    func funcB() {}
}

class Delegated2: MyDelegate {
    func funcA() { print("A2") }
    func funcB() {}
}

class BaseViewController<T: MyDelegate>: UIViewController {
    var delegate: T?

    func doSomething() {
        delegate?.funcA()
    }
}

class SomeViewController1: BaseViewController<Delegated1> {}
class SomeViewController2: BaseViewController<Delegated2> {}

class TestClass {
    let viewController1: SomeViewController1 = {
        let viewController = SomeViewController1(nibName: nil, bundle: nil)
        viewController.delegate = .init()
        return viewController
    }()

    let viewController2: SomeViewController2 = {
        let viewController = SomeViewController2(nibName: nil, bundle: nil)
        viewController.delegate = .init()
        return viewController
    }()


    // prints:
    // A1
    // A2
    func myFunc() {
        viewController1.doSomething()
        viewController2.doSomething()

    }
}

【讨论】:

  • 我需要委托协议是通用的,所以我不能用 BaseViewController 来限制它。
  • 我编辑了答案。这可能就是您正在寻找的。​​span>
猜你喜欢
  • 2014-12-07
  • 2015-11-07
  • 1970-01-01
  • 1970-01-01
  • 2018-12-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多