如何将 UIViewController 数组转换为协议类型数组

How to cast an array of UIViewControllers to an array of protocol types

我有一个 UITabBarController,其 viewControllers 都实现了协议 Refreshable。为什么我无法将选项卡栏控制器的 viewControllers 属性 解包为 Refreshable 类型的数组?

if let tabs = self.tabBarController.viewControllers as? [Refreshable] {
    // Some code
    tabs[0].refresh() // refresh() is a function in Refreshable
}

没有编译错误,但是 if let 闭包中的代码永远不会执行。我怎样才能正确地做到这一点?

添加:

这个可行,但我不喜欢它--我觉得我应该可以像上面那样表达这个。

if let tabs = self.tabBarController.viewControllers as? [UIViewController] {
    if tabs[0] is Refreshable {
        (tabs[0] as! Refreshable).refresh()
    }
}

标签栏控制器中的 viewControllers 属性 被声明为 [AnyObject]? .. 所以类型检查 as? [Refreshable] 不会通过。 相反,遍历视图控制器并检查每个视图控制器的类型:

for viewController in self.tabBarController!.viewControllers! {
    if let refreshableTab = viewController as? Refreshable {
        refreshableTab.refresh()
    }
}

如果您的数组是 [UIViewController],它不会接受类型为 Refreshable 的项目,因为其他任意 classes 可能符合它,但没有UIViewController 的属性。

换句话说,仅 Refreshable 对您的数组来说还不够强大,因为它不能保证 UIViewController 个实例。

原始表达式仅适用于数组,其中类型由协议类型指定,如下所示:

// Suppose MyClass conforms to Refreshable

let myArray: [Refreshable] = [
    MyClass(),
    MyClass(),
    MyClass(),
    MyClass(),
]

// Now you can use refresh() without problems

for item in myArray {
    item.refresh()
}

不幸的是,由于您在标签栏控制器上使用 属性,恐怕这不适合您。确保您只使用符合 Refreshable 的实例的一种可能方法是首先 filter/map 您的数组,如下所示:

let items = myarray.filter({ [=11=] is Refreshable }).map({ return [=11=] as! Refreshable })

// items will be [Refreshable]
for item in items  {
    item.refresh()
}

这里需要注意的是,如果您使用 items 类型 [Refreshable],您不能再假设它具有原始 class 的属性(即 MyClass),只有协议授予它们的那些(即本例中的 refresh())。

总结@József Vesza 的好答案,您可以执行以下操作:

if  let tabs = self.tabBarController.viewControllers as? [UIViewController],
    let refreshables = tabs.filter({ [=10=] is Refreshable }).map({ [=10=] as! Refreshable } {
        refreshables[0].refresh()
}