【问题标题】:Swift - what's the difference between metatype .Type and .self?Swift - 元类型 .Type 和 .self 有什么区别?
【发布时间】:2015-10-04 23:08:05
【问题描述】:

Swift 中元类型.Type.self 有什么区别?

.self.Type 是否返回 struct

我了解.self 可用于与dynamicType 核对。 .Type怎么用?

【问题讨论】:

标签: swift


【解决方案1】:

这是一个简单的例子:

func printType<T>(of type: T.Type) {
    // or you could do "\(T.self)" directly and
    // replace `type` parameter with an underscore
    print("\(type)") 
} 

printType(of: Int.self) // this should print Swift.Int


func printInstanceDescription<T>(of instance: T) {
    print("\(instance)")
} 

printInstanceDescription(of: 42) // this should print 42

假设每个实体由两个事物表示:

  • 类型:# entitiy name #

  • 元类型:# entity name # .Type

元类型类型是指任何类型的类型,包括类类型、结构类型、枚举类型和协议类型。

Source.

您可以很快注意到这是递归的,并且可以使用 (((T.Type).Type).Type) 等类型。

.Type 返回一个元类型的实例。

我们可以通过两种方式获取元类型的实例:

  • 在具体类型上调用.self,例如Int.self,这将创建一个 静态元类型实例Int.Type

  • 通过以下方式从任何实例中获取 dynamic 元类型实例 type(of: someInstance)

危险区域:

struct S {}
protocol P {}

print("\(type(of: S.self))")      // S.Type
print("\(type(of: S.Type.self))") // S.Type.Type
print("\(type(of: P.self))")      // P.Protocol
print("\(type(of: P.Type.self))") // P.Type.Protocol

.Protocol 是另一种仅存在于协议上下文中的元类型。也就是说,我们无法表达我们只想要P.Type。这会阻止所有通用算法使用协议元类型,并可能导致运行时崩溃。

对于更多好奇的人:

type(of:) 函数实际上由编译器处理,因为 .Protocol 创建的不一致。

// This implementation is never used, since calls to `Swift.type(of:)` are
// resolved as a special case by the type checker.
public func type<T, Metatype>(of value: T) -> Metatype { ... }

【讨论】:

  • 不客气 :) 还有一个.dynamicType。这是在运行时获取实例的最后一级类型。假设你有这个:let instance : UIView = CustomUIView() 然后你会在某个地方调用instance.dynamicType 然后你会得到CustomUIView 而不是UIView。 ;)
  • .Type 是返回一个类还是一个结构,还是两者兼而有之?
  • 不管是结构体、枚举还是类。它将返回该事物的类型。如果您有struct A {},那么A.self 将返回A
  • .Type 并不神奇,它表示您将传递/拥有特定类型的元类型,而不是实例。 .self 可以得到这个元类型。到处都有这些类型的后缀有点难以解释。 :D
  • 通过编译器魔法,我的意思是类型并不是我们可以看到定义的东西(就像我们可以使用字典或数组一样)
【解决方案2】:

它们在语法上出现在不同的地方。

在语法上必须指定类型的地方,Something.Type 是一个有效类型,对应于Something 的元类型(即类的元类)的类型。 Something.self 不是类型的有效语法。

在语法上必须编写表达式的地方,Something.self 是一个有效的表达式。它是Something.Type 类型的表达式,其值是表示Something 类型的事物(在类的情况下为“类对象”)。 Something.Type 不是有效的表达式语法。

【讨论】:

    【解决方案3】:

    首先请参阅type(of:) 上的 Apple 文档

    函数签名很有趣:

    func type<T, Metatype>(of value: T) -> Metatype
    

    用在什么地方?

    如果您正在编写/创建一个接受 type 的函数,例如UIView.Type不是实例,例如UIView()then 给你写 T.Type 作为参数的类型。它期望的参数可以是:String.selfCustomTableView.selfsomeOtherClass.self

    但是为什么函数需要类型呢?

    通常一个需要类型的函数,是一个为您实例化对象的函数。我能想到两个很好的例子:

    1. register 来自 tableview 的函数
    tableView.register(CustomTableViewCell.self, forCellReuseIdentifier: "CustomTableViewCell")
    

    请注意,您通过了CustomTableViewCell.self。如果稍后您尝试将 CustomTableViewCell 类型的 tableView 出列,但没有注册 CustomTableViewCell 类型,那么它会崩溃,因为 tableView 尚未出列/实例化任何 CustomTableViewCell 类型的 tableviewcells。

    1. decode 函数来自 JSONDecoder。示例来自链接
    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)
    

    通知try decoder.decode(GroceryProduct.self, from: json)。因为你传递了GroceryProduct.self,它知道它需要实例化一个GroceryProduct 类型的对象。如果它不能,那么它会抛出一个错误。有关JSONDecoder 的更多信息,请参阅此well written answer

    1. 作为需要类型的替代解决方法,请参阅以下问题:Swift can't infer generic type when generic type is being passed through a parameter。接受的答案提供了一个有趣的选择。

    有关内部结构及其工作原理的更多信息:

    .类型

    类、结构或枚举类型的元类型是 该类型后跟 .Type。协议类型的元类型——不是 在运行时符合协议的具体类型——是 该协议后跟 .Protocol。例如,元类型 class 类型 SomeClassSomeClass.Type 和元类型 协议 SomeProtocolSomeProtocol.Protocol

    来自苹果:metaType Type

    Under the hoodAnyClass

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

    基本上你在哪里看到AnyClassAny.TypeAnyObject.Type,因为它需要一个类型。我们看到的一个非常常见的地方是当我们想要使用register func 为我们的tableView 注册一个类时。

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

    如果你对 'Swift.' 是什么感到困惑。然后执行上述操作,然后查看来自 here

    的 cmets

    上面也可以写成:

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

    .自己

    您可以使用 postfix 自表达式将类型作为值访问。 例如, SomeClass.self 返回 SomeClass 本身,不是一个实例 SomeClass的。 SomeProtocol.self 返回 SomeProtocol 本身,不是 在运行时符合 SomeProtocol 的类型的实例。你 可以使用带有类型实例的type(of:) 表达式来访问 该实例的动态、运行时类型作为值,如下所示 示例显示:

    来自苹果:metaType Type


    游乐场代码:

    简单示例

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

    困难的例子

    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
    

    我强烈推荐阅读 Apple documentation on Types。另见here

    【讨论】:

    • 我不是一个强大的快速程序员,但我猜tableView.register(Class, forCellReuseIdentifier: "CustomTableViewCell") 是新手程序员会尝试而不是Class.self 将类型传递给函数?为什么Class 在语法上看起来更简单时需要Class.self
    • Table.Type 用于函数需要类型而不是实例时。 Table 在函数需要该类型的实例时使用。恭敬地Table.selfTable() 满足这些功能的期望。这 4 个不同的目的因此你不能照你说的做
    • 我明白为什么需要在函数签名中使用元类型,但为什么必须作为参数传递Table.self,为什么不只是Table,因为它看起来更简单,也可以成为可能。我看不出type as a value 现象的目的。
    • 哦,Table 本身仅表示“返回类型”,而不是可用作值,因此使用 type as a value 很重要。谢谢
    • @BenButterworth 我对你的问题的回答只是我刚才所说的。如果有帮助,newacct's answer below 也会回答您的问题。这只是编译器被设计用来读取的方式
    【解决方案4】:

    这是今天让我很困惑的话题之一。

    我正在编写一个通用函数:

    func foo<T: Protocol>(ofType: T.Type) {
        T.bar()
    }
    

    并尝试如下调用它:

    foo(ofType: ClassImplementingProtocol.Type) // Compiler error
    

    花了大约 30 分钟研究它为什么不起作用。然后我尝试了这个:

    foo(ofType: ClassImplementingProtocol.self) // Works
    

    事实证明 Xcode 的代码完成在显示元类型和类型之间的区别方面非常糟糕......从代码完成弹出窗口看起来 .self 和 .Type 是同一件事:

    但是它的“解释就像 im 5”是,当你有一个 Class.Type 的方法参数时,它期待一个 Class.Type 的实例。

    Class.self 返回一个 Class.Type 的实例,而 Class.Type 指的是 Class.Type...

    如果你问我,我很不清楚。

    【讨论】:

    • Class.selfClass.Type 就像 "Hello World\n"String
    【解决方案5】:

    元类型.Type

    Metatype 是一种类型,它允许您访问 Class 和 Struct[About] 类型(不是实例)的一部分,例如初始化器 class 和 static [About]属性和方法

    let var1: String = HelloWorld
    let var2: String.Type = HelloWorld.self
    

    一些实验:

    class SomeClass {
        required init() { }
        
        class func foo1() { }
        static func foo2() { }
        
        func foo3() { }
    }
    
    class SomeSubClass: SomeClass { }
    
    let a1: SomeClass = SomeClass()
    let a2: SomeClass = a1
    let a3: SomeClass = a1.self
    
    SomeClass.self.foo1() //class func
    SomeClass.foo1() //class func
    
    //static. metatype by type(class name) <class_name/structure_name>.self
    let c1: SomeClass.Type = SomeClass.self
    //dynamic. metatype by instance
    let c2: SomeClass.Type = type(of: a1)
    
    //access to type's init, class, static throught Metatype
    let d1: SomeClass = c1.self.init()
    let d2: SomeClass = c1.init()
    
    //call
    c1.foo1() //class func
    c1.foo2() //static func
    //        c1.foo3() //instance func. Instance member 'foo3' cannot be used on type 'SomeClass'
    //        c1.foo3(SomeClass()) //Expression resolves to an unused function
    
    //Sub
    // <class_name>.Type allows to save class and sunclass
    var e1: SomeClass.Type = SomeClass.self //class
    e1 = SomeSubClass.self //sub class
    
    //Any.Type allows to save class and struct
    var e2: Any.Type = SomeClass.self //class
    e2 = String.self //struct
    
    //AnyObject.Type allows to save only class
    var e3: AnyObject.Type = SomeClass.self //class
    e3 = NSString.self //class
    

    获取字符串

    let typeString = "\(SomeType.Type)"
    
    func register<T>(instance: T) {
        instanceString = String(describing: type(of: instance))
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-07-17
      • 1970-01-01
      • 1970-01-01
      • 2015-04-15
      • 2020-03-04
      • 2016-02-24
      • 1970-01-01
      相关资源
      最近更新 更多