本文首发于 Ficow Shen's Blog,原文地址: Swift self, Self, ==, === 傻傻分不清楚?。
内容概览
前言
self
和Self
==
和===
总结
前言
在2014年开源之后,Swift就在飞速地茁壮成长,内涵也越来越丰富。
对于实际使用Swift的人来说,概念多了就比较容易混淆😨,然后就会导致开发效率低下。毕竟,你需要去查这些概念的正确定义,或者你需要去写代码进行验证。self, Self, ==, === 就是比较典型的例子。
在面试别人的过程中,我发现有很多朋友分不清楚这些概念。所以,我打算根据我自己的理解来梳理一下这些概念,希望能够帮大家少走一些弯路。🌟🌟🌟
self
和 Self
实例中的 self
首先,最简单的肯定是实例中的 self
了:
如上图所示,Xcode 会告诉你这个 self
属于 TestClass
类型。
类型中的 self
:
如上图所示,Xcode 会告诉你这个 self
是 TestClass.Type
类型。稍等一下,.Type
是什么东西?
从字面意思去理解,这个 self
是 TestClass
这个类型本身。
如果为该类型定义一个静态常量(static let
),我们就可以在类型的静态方法(static func
)中去访问这个静态常量。因为,它们都属于这个类型本身。self.classLet
也就是在访问 TestClass
这个类型本身的常量 classLet
。
实体类型(concrete type)中的 Self
请看,我们可以直接通过 Self
去访问类型上的属性:
但是,这个 Self
不等于 self
。
上图中的 print
函数会打印 true
。为什么呢?
请推测一下,print(TestClass.self == self)
会打印 true
还是 false
?
请不要停止思考,更有意思的来了😹:
请问,这些 print
会分别打印什么内容?
好吧,结果是 3 行 true
。所以, 这个 Self
等同于当前这个实体类型,对吗?
抱歉,不对!!!🤭 根据 官方文档 的内容,这个 Self
的值等于 type(of: self)
。也就是说,这个值是动态获取的!
现在,让我们来看一个示例:
class TestClass {
static let classLet = 0
let instanceLet = classLet // 不能写成 self.classLet
var instanceVar = classLet
lazy var instanceLazyVar = Self.classLet // 不能写成 self.classLet
}
如果需要用类型中的属性来初始化实例中的属性,就可以参考上面这种方法。注意,不能写成 let instanceLet = self.classLet
。这样写会出现编译错误,self
是未定义的。
如果使用懒加载的属性,要注意区分 self
和 Self
。因为实例已经完成了初始化,此时 self
是有效的。
如果将 Self
用在协议中,比如:
protocol TestProtocol {
func getSelf() -> Self
}
class TestBaseClass: TestProtocol {
func getSelf() -> Self {
return self
}
}
class TestSubclass: TestBaseClass {
override func getSelf() -> Self {
return self
}
}
let base: TestBaseClass = TestBaseClass()
let sub: TestSubclass = TestSubclass()
此时,Self
是最终实现协议的那个类型。
==
和 ===
==
- 是
Equatable
协议中定义的方法:static func == (lhs: Self, rhs: Self) -> Bool
; - 否定形式:
!=
; - 支持自定义比较,规则可以由开发者自行定义比较的规则;
示例:
class MyType: Equatable {
let id: UUID
let name: String
init(id: UUID, name: String) {
self.id = id
self.name = name
}
static func == (lhs: MyType, rhs: MyType) -> Bool {
// lhs: left-hand side, rhs: right-hand side
// 也可以是: return lhs.id == rhs.id,规则由你来定义
return lhs.id == rhs.id && lhs.name == rhs.name
}
}
// 还可以这样定义:
class MyType: Equatable {
let name: String
init(name: String) {
self.name = name
}
static func == (lhs: MyType, rhs: MyType) -> Bool {
// ObjectIdentifier 不支持值类型
// 也可以这样比较: return lhs === rhs
return ObjectIdentifier(lhs) == ObjectIdentifier(rhs)
}
}
===
- 比较的是指针,所以只能用于比较引用类型;
- 否定形式:
!==
; - 不支持开发者自定义比较的规则;
示例:
NSObject() === NSObject()
相关文档:
Equivalence Operators(==)
Identity Operators(===)
总结
这些是比较常用而且比较基础的语法知识点,我们要争取理解到位,否则就会影响到开发效率。
这是 Swift 的 Revision History(文档修订历史),建议大家多关注。比如 SwiftUI
中最常见的 some
关键字就是在 Swift 5.1 中新增的 Opaque Types
。在掌握新特性的同时,不定期地去温习旧的基础知识,这样可以有效地保证自己的认知没有与现实脱节~
以上就是本文的全部内容,如有谬误,麻烦帮我指出。
如果你也有推荐阅读的内容,请留言告诉我,大家共同进步!谢谢~ 🤗