什么是.self,.Type和.Protocol?

Ah, metatypes. That’s another one for the list of things I use everyday but couldn’t explain in an interview if my life depended on it.

啊,亚型。 那是我每天使用事物清单中的另一个, 但是如果我的生活依赖它,就无法在采访中解释

Metatypes are pretty useful in Swift, and you have certainly used it on multiple occasions. Unfortunately they look pretty weird in code, which can cause some confusion when trying to understand what they really are.

元类型在Swift中非常有用,并且您肯定已经多次使用它。 不幸的是,它们在代码中看起来很奇怪,在试图理解它们的真实含义时可能会引起混乱。

I know how these weird suffixes can confuse you, but don’t worry, they’re actually pretty straightforward once you get to know the differences between each of them. But before getting into that, let’s take a step back.

我知道这些奇怪的后缀会使您感到困惑,但是请放心,一旦您了解了它们之间的区别,它们实际上就非常简单了。 但是在开始之前,让我们退后一步。

什么是元类型? (What’s a Metatype?)

If you take a look at Apple’s docs, you’ll see that a metatype is defined as being the type of a type. Wait, isn’t String a type? What could possibly be type of a String that’s already a type? SuperString??

如果看一下Apple的文档,您会看到一个元类型被定义为type的类型 。 等等,不是String类型吗? 已经是类型的字符串的类型可能是什么? 超弦

It sounds weird in theory, but that’s because we got used to Swift’s syntax which specifically hides some of these details from us in order to make the language easy to use. To understand metatypes, try to stop seeing things as types and start seeing them more as instances and classes (the usage keyword, not the object!) instead.

从理论上讲,这听起来很怪异,但这是因为我们习惯了Swift的语法,该语法专门向我们隐藏了其中的一些细节,以使该语言易于使用。 要理解元类型,请尝试停止将事物视为类型,而开始更多地将它们视为实例 (uses关键字,而不是object!)。

Considering the following snippet: How would you define SwiftRocks() and : SwiftRocks?

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

struct SwiftRocks {
static let author = "Bruno Rocha"
func postArticle(name: String) {}
}let blog: SwiftRocks = SwiftRocks()

You can say that SwiftRocks() is an object and SwiftRocks is its type, but instead, try seeing SwiftRocks() as an instance and : SwiftRocks itself as the representation of the type of an instance. After all, you can call the instance method postArticle() from blog, but you can’t access the class property author.

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

Now, how can we access author? The most common way would be through SwiftRocks.author, which will directly return you a String, but I will ask you to forget about that one for a moment. Is there another way?

现在,我们如何访问作者 ? 最常见的方式是通过SwiftRocks.author,它将直接为您返回一个String ,但是我会请您暂时忽略字符串 。 还有另一种方法吗?

I know that, Bruno! You can call type(of: blog).author!

我知道,布鲁诺! 您可以调用 type(of:blog).author

Yup! That is also correct, as type(of) transforms something an object into something that allows you to access all class properties. But have you ever tried to call just type(of: blog) to see what would happen?

对! 这也是正确的,因为type(of)将某个对象转换为允许您访问所有类属性的对象。 但是,您是否曾经尝试调用type(of:blog)来查看会发生什么?

let something = type(of: blog) // SwiftRocks.Type

One of the weird suffixes! The type of SwiftRocks is SwiftRocks.Type, which means that SwiftRocks.Type is SwiftRocks’s metatype.

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

By using Xcode’s code completion on the something property, you’ll see that a reference to a metatype allows you to use all of that type’s class properties and methods, including init():

通过对something属性使用Xcode的代码完成,您将看到对元类型的引用使您可以使用该类型的所有类属性和方法,包括init()

let author: String = something.author
let instance: SwiftRocks = something.init()

That’s very useful when you want a method to instantiate objects for you (like how UITableView cell reuse and Decodable work), access class properties or just overall do actions based on the type of an object. Doing so in a generic way is easy, as you can pass metatypes as arguments:

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

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

Metatypes can also be used in equality checks, which I personally find handy when designing factories:

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

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!")
}
}

You can define the metatype of any type, including classes, structs, enums and protocols as being the name of that type followed by .Type. In short, while SwiftRocks refers to the type of an instance (which only lets you use instance properties), the metatype SwiftRocks.Type refers to the type of class itself, which lets you use the SwiftRocks’s class properties. “type of a type” makes a lot more sense now, right?

您可以将任何类型的元类型(包括类,结构,枚举和协议)定义为该类型名称,后跟.Type 。 简而言之, SwiftRocks是指实例的类型(只允许您使用实例属性),而元类型SwiftRocks.Type是指类本身的类型,可以让您使用SwiftRocks的类属性。 “类型的类型”现在变得更有意义了,对吧?

type(of :)动态亚型vs .self静态亚型 (type(of:) Dynamic Metatypes vs .self Static Metatypes)

So type(of) returns the metatype of an object, but what happens if I don’t have an object? Xcode gives me a compiler error if I try to call create(blogType: TutorialBlogPost.Type)!

所以type(of)返回对象的元类型,但是如果我没有对象会发生什么呢? 如果我尝试调用create(blogType:TutorialBlogPost.Type), Xcode会给我一个编译器错误!

To make it short, the reason you can’t do that is the same reason why you can’t call myArray.append(String): String is the name of the type, not the value! To get a metatype as a value, you need to type the name of that type followed by .self.

简而言之,您不能执行此操作的原因与无法调用myArray.append(String)的原因相同: String是类型的名称,而不是值! 要获得一个元类型作为值,您需要键入该类型的名称,后跟 .self

If that sounds confusing, you can see it like this: Just like String is the type and “Hello World” is the value of an instance, String.Type is the type and String.self is the value of a metatype.

如果这听起来令人困惑,那么您可以这样看:就像String是类型, “ Hello World”是实例的值, String.Type是类型,而String.self是元类型的值。

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

.self is what Apple calls a static metatype — a fancy word for what is the compile time type of an object. You use that more than you expect — remember when I told you to ignore SwiftRocks.author? The reason was because writing that is the same as writing SwiftRocks.self.author.

Apple称其为.self静态元类型,这是一个奇特的词,它表示对象的编译时间类型。 您的使用超出了您的预期-还记得我告诉过您忽略SwiftRocks.author吗? 原因是因为编写与编写SwiftRocks.self.author相同。

Static metatypes are everywhere in Swift, and you implicitly use them every time you access a type’s class property directly. You might find interesting that the AnyClass type used by a table’s register(cellClass:) is just an alias for AnyObject.Type:

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

public typealias AnyClass = AnyObject.Type

On the other hand, type(of) will return a dynamic metatype, which is the metatype of the object’s real, runtime type.

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

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

The actual contents of type(of:) and its Metatype return type are compiler magic (a subject for another article), but here’s the method’s signature:

type(of :)和它的Metatype返回类型的实际内容是编译器魔术(另一篇文章的主题),但这是方法的签名:

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

In short, if the subclass of an object matters, you should use type(of) in order to have access to that subclass’s metatype. Otherwise, you can simply access the static metatype directly through (name of the desired type).self.

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

An interesting property of metatypes is that they are recursive, which means you can have meta-metatypes like SwiftRocks.Type.Type. Thankfully, for our sanity, you can’t do much with these as it’s currently impossible to write extensions for metatypes.

元类型的一个有趣的属性是它们是递归的,这意味着您可以拥有诸如SwiftRocks.Type.Type之类的元元类型 。 值得庆幸的是,出于我们的理智,您不能对它们做太多事情,因为当前无法编写元类型的扩展名。

协议元类型 (Protocol Metatypes)

Although everything said before applies to protocols, they have an important difference. The following code will not compile:

尽管前面说过的所有内容都适用于协议,但是它们之间有着重要的区别。 以下代码将无法编译:

protocol MyProtocol {}
let metatype: MyProtocol.Type = MyProtocol.self // Cannot convert value of...

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.

其原因是,在协议的情况下,MyProtocol.Type并不是指协议本身的元类型,但无论何种类型的元类型为继承该协议。 苹果称其为存在性元类型

protocol MyProtocol {}
struct MyType: MyProtocol {}
let metatype: MyProtocol.Type = MyType.self // Now works!

In this case, metatype only has access to MyProtocol class properties and methods, but MyType’s implementations will be called. To get the concrete metatype of the protocol type itself, you can use the .Protocol suffix. That’s basically the same as using .Type on other types.

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

let protMetatype: MyProtocol.Protocol = MyProtocol.self

Because we’re referring to the uninherited protocol itself, there’s nothing you can really do with protMetatype besides simple equality checks like protMetatype is MyProtocol.Protocol. If I had to make a guess, I would say that a protocol’s concrete metatype’s purpose is more about making protocols work in the compiler side of things, which is likely why we never see it in iOS projects.

因为我们指的是未继承的协议本身,所以除了简单的等式检查(例如protMetatype为MyProtocol.Protocol)之外,您对protMetatype 所做的工作实际上是无能为力的 。 如果我不得不猜测,我会说协议的具体元类型的目的更多是使协议在事物的编译器方面起作用,这很可能就是为什么我们在iOS项目中从未看到过。

结论:亚型的更多用途 (Conclusion: More Uses for Metatypes)

Representing a type through a metatype can help you build very intelligent and type-safe generic systems. Here’s an example of how we use them in deep link handlers to prevent having to deal with strings directly:

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

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

And as a more recent example, here’s how we use metatypes to represent and retrieve information about A/B tests (called “Experiments”):

作为最近的示例,这是我们使用元类型来表示和检索有关A / B测试的信息(称为“实验”)的方式:

if ExperimentManager.get(HomeExperiment.self)?.showNewHomeScreen == true {
//Show new home
} else {
//Show old home
}// Experiment Managerpublic 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
}

翻译自: https://medium.com/better-programming/what-are-self-type-and-protocol-9f1fb4fda616

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值