Swift - what's the difference between metatype .Type and .self?

前端 未结 4 740
一生所求
一生所求 2020-12-02 10:52

What\'s the difference between metatype .Type and .self in Swift?

Do .self and .Type return a struct

4条回答
  •  温柔的废话
    2020-12-02 11:09

    Where is it used?

    If you are writing/creating a function that accepts a type e.g. UIView.Type, not an instance e.g. UIView()then to you would write T.Type as the type of the parameter. What it expects as a parameter can be: String.self, CustomTableView.self, someOtherClass.self.

    But why would a function ever need a type?

    Normally a function which requires a type, is a function that instantiates objects for you. I can think of two good examples:

    1. register function from tableview
    tableView.register(CustomTableViewCell.self, forCellReuseIdentifier: "CustomTableViewCell")
    

    Notice that you passed CustomTableViewCell.self. If later on you try to dequeue a tableView of type CustomTableViewCell but didn't register CustomTableViewCell type then it would crash because the tableView hasn't dequeued/instantiated any tableviewcells of CustomTableViewCell type.

    1. decode function from JSONDecoder. Example is from the link
    struct GroceryProduct: Codable {
        var name: String
        var points: Int
        var description: String?
    }
    
    let json = """
    {
        "name": "Durian",
        "points": 600,
        "description": "A fruit with a distinctive scent."
    }
    """.data(using: .utf8)!
    
    let decoder = JSONDecoder()
    let product = try decoder.decode(GroceryProduct.self, from: json)
    
    print(product.name)
    

    Notice try decoder.decode(GroceryProduct.self, from: json). Because you passed GroceryProduct.self it knows that it needs to instantiate an object of type GroceryProduct. If it can't then it would throw an error. For more on JSONDecoder see this well written answer

    1. As an alternate workaround for where types are needed see the following question: Swift can't infer generic type when generic type is being passed through a parameter. The accepted answer offers an intersting alternative.

    More about the internals and how it works:

    .Type

    The metatype of a class, structure, or enumeration type is the name of that type followed by .Type. The metatype of a protocol type—not the concrete type that conforms to the protocol at runtime—is the name of that protocol followed by .Protocol. For example, the metatype of the class type SomeClass is SomeClass.Type and the metatype of the protocol SomeProtocol is SomeProtocol.Protocol.

    From Apple : metaType Type

    Under the hood AnyClass is

    typealias AnyClass = AnyObject.Type // which is why you see T.Type 
    

    Basically where ever you see AnyClass, Any.Type, AnyObject.Type, its because it's in need of a type. A very very common place we see it is when we want to register a class for our tableView using register func.

    func register(_ cellClass: Swift.AnyClass?, forCellReuseIdentifier identifier: String)
    

    If you are confused as to what does 'Swift.' do then above, then see the comments from here

    The above could have also been written as:

    func register(_ cellClass: AnyObject.Type, forCellReuseIdentifier identifier: String)
    

    .self

    You can use the postfix self expression to access a type as a value. For example, SomeClass.self returns SomeClass itself, not an instance of SomeClass. And SomeProtocol.self returns SomeProtocol itself, not an instance of a type that conforms to SomeProtocol at runtime. You can use a type(of:) expression with an instance of a type to access that instance’s dynamic, runtime type as a value, as the following example shows:

    From Apple : metaType Type


    Playground code:

    Easy example

    struct Something {
        var x = 5
    }
    
    let a = Something()
    type(of:a) == Something.self // true
    

    Hard example

    class BaseClass {
        class func printClassName() {
            print("BaseClass")
        }
    }
    class SubClass: BaseClass {
        override class func printClassName() {
            print("SubClass")
        }
    }
    
    
    let someInstance: BaseClass = SubClass()
    /*                      |                |
                        compileTime       Runtime
                            |                | 
    To extract, use:       .self          type(of)
    
      Check the runtime type of someInstance use `type(of:)`: */
    
    print(type(of: someInstance) == SubClass.self) // True
    print(type(of: someInstance) == BaseClass.self) // False
    
     /* Check the compile time type of someInstance use `is`: */
    
    print(someInstance is SubClass) // True
    print(someInstance is BaseClass) // True
    

    I highly recommend to read Apple documentation on Types. Also see here

提交回复
热议问题