【问题标题】:Swift 3, is the ".self" in a metatype issue actually correct?Swift 3,元类型问题中的“.self”真的正确吗?
【发布时间】:2017-02-11 14:55:33
【问题描述】:

我有一个扩展来遍历视图控制器链(甚至通过容器视图,这非常方便)

public extension UIViewController   // go up to a certain class
    {
    public func above<T>(_ : T.Type)->(T)
        {
        var p:UIResponder = self
        repeat { p = p.next! } while !(p is T)
        return p as! T
        }
    }

(除此之外,请注意,Swift3 需要 p.next 上的“!”:不幸的是,我不确定为什么。)

所以,假设你有一个视图控制器类“General”,你可以

self.above(General).clickedHamburgerMenuButton()

它会找到您上方的第一个“General”。一切都很好,但使用 Swift 3 你会收到这个警告......

缺少“.self”以引用“General”类型的元类型

好像想要这个

self.above(General.self).clickedHamburgerMenuButton()

1) 将General 更改为General.self 似乎......很危险......实际上它是否安全,并且 含义与 Swift 中的General 相同

2) 在扩展中

    public func above<T>(_ : T.Type)->(T)

为什么这是一个元类型?我是否“做错了什么”并让它要求一个元类型而不仅仅是一个类型?

2) “元类型”到底是什么? (我真的无法在任何地方找到它的解释。)也就是说,除了“类本身”之外,“一般”可能意味着什么。 (不是实例,也不是静态实例,或其他任何东西......)

【问题讨论】:

  • 能够为单参数函数省略 .self 是 Swift 3 之前的一个错误,请参阅 swift class as parameter without .self。另请参阅this Q&A,了解为什么需要.self。至于关于元类型的信息,see this part of the documentation :)。在您的示例中,T.Type 是元类型
  • 感谢@Hamish,这是一个美丽且信息丰富的评论:)
  • 顺便说一句,当然你的方法可以在没有强制解包的情况下编写,注意如果没有找到匹配的响应者,它会崩溃。
  • 嗨@MartinR - 谢谢。如果我完全理解“强制展开”是什么,我将能够开始理解您的评论.. :-) thx

标签: generics swift3 metatype


【解决方案1】:

(顺便说一句,注意,Swift3 需要 p.next 上的“!”:不幸的是,我不确定为什么。)

因为.next 返回一个Optional,但p 不是可选的。如果您的响应者用完,这将崩溃。

1) 将 General 更改为 General.self 似乎......很危险......实际上它是否安全并且与 Swift

我很惊讶在没有 .self 的早期版本的 Swift 中工作,但是是的,需要 .self 才能直接引用元类型。引用元类型在 Swift 中有些少见,如果无意中完成,可能会导致令人惊讶的行为,因此需要额外的语法来表示“是的,我真的是指类型。”

为什么这是一个元类型?我是否“做错了什么”并让它要求一个元类型而不仅仅是一个类型?

你做得对。

2) “元类型”到底是什么?

类型的类型。元类型的实例是一种类型。考虑:

func f(x: Int)

要调用它,您需要传递一个Int 的实例。所以类似:

func f<T>(x: T.Type)

要调用它,您需要传递元类型T.Type 的一个实例,它是一个类型。


无关,但我可能会按照这些思路重新考虑这段代码。首先,能够将响应者链视为一个序列很方便。这是一种方法:

public extension UIResponder {
    public func nextResponders() -> AnySequence<UIResponder> {
        guard let first = next else { return AnySequence([]) }
        return AnySequence(sequence(first: first, next: { $0.next }))
    }
}

然后获得与类型匹配的下一个响应者是更清晰的 IMO(并且适用于任何响应者链):

public extension UIResponder {
    public func firstResponder<T: UIResponder>(ofType _: T.Type)-> T? {
        return nextResponders()
            .flatMap { $0 as? T }
            .first
    }
}

...

self.firstResponder(ofType: General.self)?.clickedHamburgerMenuButton()

【讨论】:

  • TBC 如果我没记错的话,你的 nextResponders 实际上会返回整个响应者链 .. 所有类。 (实际上,在那里使用递归。)然后,您的 firstResponder 只需选择所有所需的类(flatMap)并返回其中的第一个(.first)。太棒了
  • 是的,nextResponders 懒惰地返回整个响应者链(因此在请求它们之前它实际上不会计算整个链)。不过,它不是递归的; sequence 是迭代的。
  • 哦,我明白了...next: { $0.next } 正在放入所有这些...野生
猜你喜欢
  • 2014-09-09
  • 2023-03-21
  • 1970-01-01
  • 2014-08-23
  • 1970-01-01
  • 1970-01-01
  • 2015-10-04
  • 1970-01-01
  • 2018-04-25
相关资源
最近更新 更多