前言
大家好,我是一牛,今天想和大家分享的是Swift的协变Self。在Swift中,协变(Covariant)Self是一种设计模式,它允许协议和基类的方法返回当前类型,而不是父类或者协议类型。这种特性可以让返回类型与调用者的类型保持一致,提高了代码的灵活性,在继承和多态的场景下应用广泛。
测验
按照惯例,我们还是以一道小题目作为今天的开胃菜。
protocol SomeProtocol {
func doSomething()
}
extension SomeProtocol {
func doSomething() {
print(Self.self)
}
}
class Base: SomeProtocol {
func doAnotherThing() {
print(Self.self)
}
}
class SomeClass: Base {}
// 请给出答案
(SomeClass() as Base).doAnotherThing()
// SomeClass or Base ??
(SomeClass() as Base).doSomething()
// SomeClass or Base ??
如果你一眼就看出答案,相信你已经协变Self掌握得不错;但是如果你感到模棱两可,请跟我一起继续学习本篇文章吧。(答案暂且不表,文末有解析!)
协议中的Self
protocol Copyable {
func copy() -> Self
}
这里的Self相当于一个占位符,在协议中定义返回 Self 的方法,可以确保符合协议的类型在实现时返回自身类型。
struct Book: Copyable {
var number = UUID()
func copy() -> Self {
return Self.init(number: number)
}
}
这里出现的Self可以替换成Book, 这是因为结构体无法被继承,Self 指代就是 Book。
protocol AnotherCopyable {
associatedtype Item
func copy() -> Item
}
我们也可以使用关联类型来实现,相比关联类型,Self表达更简洁
类中的Self
class Animal: Copyable {
var name: String
required init(name: String) {
self.name = name
}
func copy() -> Self {
return Self.init(name: name)
}
}
因为Animal可以被继承,这里和结构体不一样,返回值Self不能替换成Animal。而且使用Self初始化实例时,必须调用一个指定构造器进行初始化。这里因为由于Animal可以被继承,如果子类没有实现指定构造器的话,子类copy会导致构造失败。
Swift类中的Self和OC的instancetype非常类似,此时Self的类型还不能确定,它是由调用端的动态类型决定的。
答案
(SomeClass() as Base).doAnotherThing()
// 输出 `SomeClass`
(SomeClass() as Base).doSomething()
// 输出 `Base`
解析 方法doSomething是协议扩展,它的类型是由调用端的静态类型决定的,也就是Base, 而方法doAnotherThing是类中定义的,所以Self的实际类型是由调用端的实际类型决定的。
结语
创作不易,如果你感到文章对你有用的话,欢迎点赞收藏,有问题也可以私信。 祝大家生活愉快!