Swift -元类型.Type和.self之间的区别是什么?

c0vxltue  于 2022-12-17  发布在  Swift
关注(0)|答案(5)|浏览(166)

Swift中的元类型.Type.self有什么区别?
.self.Type是否返回struct
我知道.self可以用来检查dynamicType,那么.Type是怎么使用的呢?

avwztpqn

avwztpqn1#

下面是一个简单的例子:

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返回元类型的示例。
有两种方法可以获得元类型的示例:

  • Int.self这样的具体类型上调用.self,这将创建一个static元类型示例Int.Type
  • 通过type(of: someInstance)从任何示例获取dynamic元类型示例。

危险区域:

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 { ... }
hmmo2u0o

hmmo2u0o2#

首先,请在type(of:)上查看Apple文档
函数签名很有趣:

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

在哪里使用

如果您正在编写/创建一个接受 type(例如UIView.Type)而不接受示例(例如UIView())的函数,则应将T.Type作为参数的类型。它期望的参数可以是:一米四氮一x一米五氮一x一米六氮一x。

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

  • 通常 * 一个需要类型的函数,是一个为你示例化对象的函数。我能想到几个例子:

1.来自表格检视的register函数

tableView.register(CustomTableViewCell.self, forCellReuseIdentifier: "CustomTableViewCell")

请注意,您传递了CustomTableViewCell.self。如果稍后您尝试将CustomTableViewCell类型的tableView出队,但没有注册CustomTableViewCell类型,则它将崩溃,因为tableView尚未出队/示例化任何CustomTableViewCell类型的tableView单元格。

  1. JSONDecoder中的decode函数。示例来自链接
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.尝试查找某个类类型的值。示例尝试在navigationController的所有视图控制器中查找某个类型的视图控制器:

func popBackTo<T>(type: T.Type, in nav: UINavigationController? = nil, completion: ((T?) -> Void)? = nil) {
    let nav = window?.rootViewController as? UINavigationController
    guard let nav = nav, let destinationVC = nav.viewControllers.first(where: { $0 is T }) else {
        return
    }
    nav.popToViewController(destinationVC, animated: true)
}

# Example call site:
popBackTo(LoginVC.self)

1.作为需要类型的情况的替代解决方法,请参阅以下问题:Swift can't infer generic type when generic type is being passed through a parameter.接受的答案提供了一个有趣的选择。
有关内部组件及其工作原理的更多信息:

.类型

类、结构或枚举类型的元类型是该类型的名称后跟. Type。协议类型的元类型(不是在运行时符合协议的具体类型)是该协议的名称后跟. Protocol。例如,类型X1 M15 N1 X的元类型是X1 M16 N1 X,并且协议X1 M17 N1 X的元类型是X1 M18 N1 X。
来自苹果:类型
Under the hoodAnyClass

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

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

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

如果你对“Swift.”做什么感到困惑,那么请参见上面的评论
上述内容也可以写成:

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

.自我

可以使用 * 后缀 * self表达式将类型作为值访问。例如,SomeClass.self返回SomeClass本身,而不是SomeClass的示例。SomeProtocol.self返回SomeProtocol本身,而不是运行时符合SomeProtocol的类型的示例。可以将type(of:)表达式与类型的示例一起使用,以访问该示例的动态、执行阶段型别当做值,如下列范例所示:
来自苹果:类型
Playground代码:

简单示例

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

我强烈建议阅读苹果关于类型的文档。另请参见here

laawzig2

laawzig23#

它们在语法上出现在不同的位置。
在语法上必须指定类型的地方,Something.Type是有效类型,对应于Something的元类型(类的元类)。Something.self不是类型的有效语法。
从语法上讲,在必须编写表达式的地方,Something.self是一个有效的表达式,它是Something.Type类型的表达式,值是表示Something类型的东西(在类的情况下是“类对象”)。Something.Type不是有效的表达式语法。

dphi5xsq

dphi5xsq4#

这是今天把我搞糊涂的主题之一。
我在写一个泛型函数:

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

试着这样称呼它:

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

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

foo(ofType: ClassImplementingProtocol.self) // Works

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

但是它的“解释像im 5”是,当你有一个Class.Type的方法参数时,它需要一个Class.Type的示例。
self返回Class.Type的示例,而Class.Type引用Class.Type ...
如果你问我的话我很不清楚。

yzuktlbb

yzuktlbb5#

元类型。类型

Metatype是一种类型,允许您访问****Class和Struct**About(https://stackoverflow.com/a/59219141/4770877)类型(非示例)的部分内容,如初始化项class和静态About(https://stackoverflow.com/a/59236762/4770877)属性和方法

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

一些实验:
x一个一个一个一个x一个一个二个x
获取字符串

let typeString = "\(SomeType.Type)"

func register<T>(instance: T) {
    instanceString = String(describing: type(of: instance))
}

相关问题