【问题标题】:How do I declare a variable that has a type and implements a protocol?如何声明具有类型并实现协议的变量?
【发布时间】:2014-10-02 13:57:56
【问题描述】:

我的应用有一个详细视图控制器的协议,声明它们必须有一个viewModel 属性:

protocol DetailViewController: class {
    var viewModel: ViewModel? {get set}
}

我还有几个不同的类实现了该协议:

class FormViewController: UITableViewController, DetailViewController {
    // ...
}

class MapViewController: UIViewController, DetailViewController {
    // ...
}

我的主视图控制器需要一个可以设置为任何实现DetailViewController 协议的UIViewController 子类的属性。

很遗憾,我找不到任何有关如何执行此操作的文档。在 Objective-C 中这将是微不足道的:

@property (strong, nonatomic) UIViewController<DetailViewController>;

似乎 Swift 中没有任何可用的语法来执行此操作。我最接近的是在我的类定义中声明一个泛型:

class MasterViewController<T where T:UIViewController, T:DetailViewController>: UITableViewController {
    var detailViewController: T?
    // ...
}

然后我收到一条错误消息,提示“类 'MasterViewController' 没有实现其超类的必需成员”

这似乎在 Swift 中应该和在 Objective-C 中一样容易,但我在任何地方都找不到任何建议我如何去做的事情。

【问题讨论】:

标签: swift protocols


【解决方案1】:

从 Swift 4 开始,您现在可以这样做了。

Swift 4 实现了SE-0156(类和子类型存在)。

这个Objective-C语法的等价物:

@property (strong, nonatomic) UIViewController<DetailViewController> * detailViewController;

现在在 Swift 4 中看起来像这样:

var detailViewController: UIViewController & DetailViewController

基本上,您可以定义一个变量符合的类,以及它实现的 N 个协议。有关详细信息,请参阅链接文档。

【讨论】:

    【解决方案2】:

    我认为您可以通过向UIViewController 添加(空)扩展名,然后使用空扩展名和DetailViewController 的组合协议指定您的detailViewController 属性来实现。像这样:

    protocol UIViewControllerInject {}
    extension UIViewController : UIViewControllerInject {}
    

    现在UIViewController 的所有子类都满足协议UIViewControllerInject。然后,简单地说:

    typealias DetailViewControllerComposed = protocol<DetailViewController, UIViewControllerInject>
    
    class MasterViewController : UITableViewController {
      var detailViewController : DetailViewControllerComposed?
      // ...
    }
    

    但是,这并不是特别“自然”。

    === 编辑、添加 ===

    实际上,如果您使用我建议的UIViewControllerInject 定义您的DetailViewController,您可以做得更好。像这样:

    protocol UIViewControllerInject {}
    extension UIViewController : UIViewControllerInject {}
    
    protocol DetailViewController : UIViewControllerInject { /* ... */ }
    

    现在您不需要显式编写某些内容(我的 DetailViewControllerComposed),并且可以使用 DetailViewController? 作为 detailViewController 的类型。

    【讨论】:

    • 我对每个 UIViewController 子类都有一个 viewModel 属性并不感到非常兴奋,但它至少可以编译 :)
    • 不,我不是这么说的。添加到 UIViewController 的协议是一个空的
    • 噢噢噢。非常聪明:)。
    • @FrankSchmitt ,如果我希望 var detailViewController: DetailViewController 严格地成为 UIViewController 的子类怎么办?就像 Objective-C 中的声明 @property (strong, nonatomic) UIViewController&lt;DetailViewController&gt; detailViewController; 一样清晰。斯威夫特有可能做到这一点吗?
    • 这解决了单向问题 - 但是当您在代码中的某处传递了 UIViewControllerInject 时,如果没有强制转换 (as!),您将无法在其上调用任何 UIViewController 方法。不幸的是,我相信目前没有办法像在 ObjC 中那样将变量声明为符合特定协议的特定类。当然,您可以在UIViewControllerInject 协议中从UIViewController 声明您需要的所有方法。
    【解决方案3】:

    另一种方法是为实现协议的适当 UIKit 视图控制器引入中间基类:

    class MyUIViewControler : UIViewController, DetailViewController ...
    class MyUITableViewController : UITableViewController, DetailViewController ...
    

    然后你从这些视图控制器继承你的视图控制器,而不是 UIKit 的。

    这也不自然,但它不会像 GoZoner 建议的那样强制您的所有 UIViewControllers 满足 UIViewControllerInject 协议。

    【讨论】:

    • 我会走那条路,但我不确定如何声明一个可以是MyUIViewControllerMyUITableViewController 类型的属性。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多