Swift 3, is the “.self” in a metatype issue actual

2019-02-24 22:42发布

I have an extension to walk up the view controller chain (even through container views, which is very handy)

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
        }
    }

(Aside, NB, Swift3 needs the "!" on p.next: unfortunately I'm not sure exactly why.)

So, say you have a view controller class "General", you can

self.above(General).clickedHamburgerMenuButton()

and it will find the first "General" above you. All fine but with Swift 3 you get this warning.......

Missing '.self' for reference to metatype of type 'General'

It seems to want this

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

1) It seems ... dangerous ... to change General to General.self - in fact is it safe and is the meaning the same as General in Swift <3 ?

2) in the extension

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

why is that a metatype? Did I "do something wrong" and make it ask for a metatype rather than just a type?

2) What the hell is a "metatype"? (I can't, really, find it explained as such anywhere.) That is to say, what can "general" possibly mean there other than "the class itself". (Not an instance, or a static instance, or anything else...)

1条回答
Summer. ? 凉城
2楼-- · 2019-02-24 23:27

(Aside, NB, Swift3 needs the "!" on p.next: unfortunately I'm not sure exactly why.)

Because .next returns an Optional, but p is not optional. This will crash if you run out of responders.

1) It seems ... dangerous ... to change General to General.self - in fact is it safe and is the meaning the same as General in Swift <3 ?

I'm surprised that worked in earlier versions of Swift without the .self, but yes, .self is required in order to directly reference a metatype. Referencing metatypes is somewhat rare in Swift, and can lead to surprising behaviors if done unintentionally, so it requires an extra piece of syntax to say "yes, I really mean the type."

why is that a metatype? Did I "do something wrong" and make it ask for a metatype rather than just a type?

You did this correctly.

2) What the hell is a "metatype"?

The type of a type. An instance of a metatype is a type. Consider:

func f(x: Int)

To call this, you pass an instance of Int. So similarly:

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

To call this, you pass an instance of the metatype T.Type, which is a type.


Unrelated, but I would probably rethink this code along these lines. First, it is convenient to be able to treat the responder chain as a sequence. Here's one way to do that:

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

Then getting the next responder that matches a type is cleaner and clearer IMO (and works on any responder chain):

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()
查看更多
登录 后发表回答