What is Metatype?

如果你看一下苹果的文档,你会发现元类型(Metatype) 被定义为类型的类型(type of a type )。但是,String不是类型吗?什么可能是已经是类型的String的类型?SuperString??

这在理论上听起来很奇怪,但这是因为我们已经习惯了Swift的语法,它特别隐藏了一些细节,以使语言易于使用。要理解元类型,请尝试停止将事物视为类型,而是开始将它们视为实例(instances)类(classes)(使用关键字,而不是对象!)。

考虑以下代码片段:如何定义SwiftRocks(): SwiftRocks

swift 复制代码
struct SwiftRocks {
    static let author = "Bruno Rocha"
    func postArticle(name: String) {}
}

let blog: SwiftRocks = SwiftRocks()

你可以说SwiftRocks()是一个对象,SwiftRocks是它的类型。但是相反,尝试将SwiftRocks()视为实例 ,并将: SwiftRocks本身视为实例的类型的表示。毕竟,你可以从blog调用实例方法postText(),但是你无法访问类属性author

现在,我们如何访问author?最常见的方法是通过SwiftRocks.author直接返回一个字符串,但请先暂时忘记这个,还有其他方法吗?

scss 复制代码
type(of: blog).author

是的!type(of: )将对象转换为允许您访问所有类属性的对象。但是你有没有试过调用type(of: blog)来看看会发生什么?

scss 复制代码
let something = type(of: blog) // SwiftRocks.Type

奇怪的后缀之一!SwiftRocks的类型是SwiftRocks.Type,这意味着SwiftRocks.TypeSwiftRocks的元类型

在Xcode中观察something,可以发现对元类型的引用 允许您使用该类型的所有类属性和方法,包括init()

ini 复制代码
let author: String = something.author
let instance: SwiftRocks = something.init()

当您需要一个方法来为您实例化对象(例如UITableView的cell重用Decodable时)、访问类属性或仅基于对象类型执行操作时,这非常有用。以通用方式执行此操作很容易,因为您可以将元类型作为参数传递:

swift 复制代码
func createWidget<T: Widget>(ofType: T.Type) -> T {
    let widget = T.init()
    myWidgets.insert(widget)
    return widget
}

元类型也可以用于相等性检查,我个人在设计工厂时觉得这很方便。

swift 复制代码
func create<T: BlogPost>(blogType: T.Type) -> T {
    switch blogType {
    case is TutorialBlogPost.Type:
        return blogType.init(subject: currentSubject)
    case is ArticleBlogPost.Type:
        return blogType.init(subject: getLatestFeatures().random())
    case is TipBlogPost.Type:
        return blogType.init(subject: getKnowledge().random())
    default:
        fatalError("Unknown blog kind!")
    }
}

你可以定义任何类型的元类型,包括类、结构体、枚举和协议,作为该类型的名称后缀.Type

简而言之,SwiftRocks指的是实例的类型,它只允许你使用实例属性;元类型SwiftRocks. Type指的是类本身的类型,它允许你使用SwiftRocks的类属性。"type of a type" 现在更有意义了,对吧?

type(of:)动态元类型 vs .self静态元类型

所以type(of:)返回对象的元类型,但是如果我没有对象会发生什么?当尝试执行下面的命令时,Xcode会提示我们编译错误:

scss 复制代码
create(blogType: TutorialBlogPost.Type)

简而言之,不能这样做的原因与不能调用myArray.append(String)的原因相同。String是类型的名称,而不是值!要将元类型作为值,您需要键入该类型的Name+.self

rust 复制代码
myArray.append(String.self)

如果这听起来令人困惑,你可以这样理解:就像String是类型,"Hello World" 是实例的值一样;String.Type是类型,String.self是元类型的值。

原文:String.Type is the type and String.self is the value of a metatype

swift 复制代码
let intMetatype: Int.Type = Int.self
//
let widget = createWidget(ofType: MyWidget.self)
tableView.register(MyTableViewCell.self, forReuseIdentifier: "myCell")

.self是苹果所谓的静态元类型 ,它指的是对象的编译时类型。你使用它的次数比你预期的要多。还记得开头时我们要求忽略的SwiftRocks.author吗?它还可以写成是SwiftRocks.self.author

静态元类型在Swift中无处不在,每次直接访问类型的class属性时都隐式使用它们。您可能会发现有趣的是,table中的register(cellClass:)使用的AnyClass类型只是AnyObject.Type的别名。

swift 复制代码
public typealias AnyClass = AnyObject.Type

另一方面,type(of:)将返回一个动态元类型,它是对象的真实运行时类型的元类型。

python 复制代码
let myNum: Any = 1 // Compile time type of myNum is Any, but the runtime type is Int.
type(of: myNum) // Int.type

type(of:)和它的返回类型Metatype是编译器魔法compiler magic,这是方法的声明:

swift 复制代码
func type<T, Metatype>(of value: T) -> Metatype {}

简而言之,如果对象的子类很重要,你应该使用type(of:)来访问该子类的元类型。否则,你可以直接通过.self访问静态元类型。

元类型的一个有趣特性是它们是递归的,这意味着您可以拥有元类型SwiftRock.Type.Type,但幸运的是,swift限制里、了不能对其做太多事情,目前不能为元类型编写扩展。

协议元类型(Protocol Metatypes)

尽管之前所说的一切都适用于协议,但它们有一个重要的区别。以下代码将无法编译:

swift 复制代码
protocol MyProtocol {}
let metatype: MyProtocol.Type = MyProtocol.self // Cannot convert value of...

原因是在协议的上下文中,MyProtocol.Type不引用协议自己的元类型,而是任何继承该协议的元类型。苹果称之为existential metatype

The reason for that is that in the context of protocols, MyProtocol.Type doesn't refer to the protocol's own metatype, but the metatype of whatever type is inheriting that protocol. Apple calls this an existential metatype.

swift 复制代码
protocol MyProtocol {}
struct MyType: MyProtocol {}
let metatype: MyProtocol.Type = MyType.self // Now works!

在这种情况下,metatype只能访问MyProtocol类属性和方法,但会调用MyType的实现。要获取协议类型本身的具体元类型 ,可以使用.Protocol后缀。这与在其他类型上使用.Type基本相同。

swift 复制代码
let protMetatype: MyProtocol.Protocol = MyProtocol.self

因为我们指的是未继承的协议本身,所以除了简单的相等检查之外,protMetatype真的没有什么可以做的,比如protMetatype is MyProtocol.Protocol。如果我不得不猜测,我会说协议的具体元类型的目的更多地是让协议在编译器方面工作,这可能是我们从未在iOS项目中看到它的原因。

总结:Metatypes的更多用途

通过元类型表示类型可以帮助您构建非常智能和类型安全的泛型系统。以下是我们如何在深度链接处理程序中使用它们以防止直接处理字符串的示例:

swift 复制代码
public protocol DeepLinkHandler: class {
    var handledDeepLinks: [DeepLink.Type] { get }
    func canHandle(deepLink: DeepLink) -> Bool
    func handle(deepLink: DeepLink)
}

public extension DeepLinkHandler {
    func canHandle(deepLink: DeepLink) -> Bool {
        let deepLinkType = type(of: deepLink)
        //Unfortunately, metatypes can't be added to Sets as they don't conform to Hashable!
        return handledDeepLinks.contains { $0.identifier == deepLinkType.identifier }
    }
}

//

class MyClass: DeepLinkHandler {
    var handledDeepLinks: [DeepLinks.Type] {
        return [HomeDeepLink.self, PurchaseDeepLink.self]
    }

    func handle(deepLink: DeepLink) {
        switch deepLink {
        case let deepLink as HomeDeepLink:
           //
        case let deepLink as PurchaseDeepLink:
           //
        default:
           //
        }
    }
}

一个更贴切的例子,以下是我们如何使用元类型来表示和检索有关A/B实验的信息:

swift 复制代码
if ExperimentManager.get(HomeExperiment.self)?.showNewHomeScreen == true {
    //Show new home
} else {
    //Show old home
}

// Experiment Manager

public static func get<T: Experiment>(_ experiment: T.Type) -> T? {
    return shared.experimentDictionary[experiment.identifier] as? T
}

public static func activate(_ experiment: Experiment) {
    shared.experimentDictionary[type(of: experiment).identifier] = experiment
}
相关推荐
幸福回头3 天前
ms-swift 代码推理数据集
llm·swift
不二狗3 天前
每日算法 -【Swift 算法】Two Sum 问题:从暴力解法到最优解法的演进
开发语言·算法·swift
struggle20255 天前
适用于 iOS 的 开源Ultralytics YOLO:应用程序和 Swift 软件包,用于在您自己的 iOS 应用程序中运行 YOLO
yolo·ios·开源·app·swift
一丝晨光6 天前
数值溢出保护?数值溢出应该是多少?Swift如何让整数计算溢出不抛出异常?类型最大值和最小值?
java·javascript·c++·rust·go·c·swift
Swift社区6 天前
Swift实战:如何优雅地从二叉搜索树中挑出最接近的K个值
开发语言·ios·swift
fydw_7156 天前
大语言模型RLHF训练框架全景解析:OpenRLHF、verl、LLaMA-Factory与SWIFT深度对比
语言模型·swift·llama
文件夹__iOS6 天前
深入浅出 iOS 对象模型:isa 指针 与 Swift Metadata
ios·swift
I烟雨云渊T8 天前
iOS实名认证模块的具体实现过程(swift)
ios·cocoa·swift
Swift社区9 天前
LeetCode 270:在二叉搜索树中寻找最接近的值(Swift 实战解析)
算法·leetcode·swift
I烟雨云渊T9 天前
iOS瀑布流布局的实现(swift)
开发语言·ios·swift