这里每天分享一个 iOS 的新知识,快来关注我吧
前言
昨天在 Code Review 的时候,一个同事突然问我:"这里为什么要用 Self.self 而不是 self?"
虽然天天在写这些代码,但要清清楚楚地解释这几个概念的区别,还真有点费劲。
你有没有过这种经历?写代码的时候,self、Self、Self.self 这几个家伙总是让人傻傻分不清楚。即使是写了好几年 Swift 的老鸟,有时候也会停下来想想:"这里该用哪个来着?"
今天就来好好聊聊这几个看起来很像、但实际上完全不同的概念。
self:指向实例本身
先说最常见的 self
(小写的 s)。这个最简单,它就是指向当前实例的引用。
class Person {
var name: String
init(name: String) {
// 这里的 self.name 指的是实例属性
// 而 name 是传入的参数
self.name = name
}
func introduce() {
print("大家好,我叫 \(self.name)")
// 其实这里的 self 可以省略
print("大家好,我叫 \(name)")
}
}
let xiaoming = Person(name: "小明")
xiaoming.introduce()
什么时候必须用 self 呢?主要是这两种情况:
1. 参数名和属性名相同时
struct Point {
var x: Double
var y: Double
mutating func move(x: Double, y: Double) {
// 必须用 self 来区分属性和参数
self.x += x
self.y += y
}
}
2. 在闭包中引用实例属性时
class ViewController {
var count = 0
func setupTimer() {
Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { _ in
// 闭包中必须显式使用 self
self.count += 1
print("计数: \(self.count)")
}
}
}
Self:类型的占位符
Self
(大写的 S)就比较高级了,它是一个类型占位符,主要在协议中使用。
继续用生活中的例子:假设你在定义一个"可复制"的规则,你会说:"任何可复制的东西,都应该能复制出一个和自己一样类型的东西"。这里的"和自己一样类型"就是 Self。
protocol Duplicatable {
// Self 表示遵循协议的具体类型
func duplicate() -> Self
}
class Document: Duplicatable {
var content: String
init(content: String) {
self.content = content
}
// 这里返回的是 Document 类型
func duplicate() -> Self {
// 需要用 type(of:) 来创建相同类型的实例
return type(of: self).init(content: self.content)
}
requiredinit(content: String) {
self.content = content
}
}
Self 的妙处在于它的多态性。不同的类型遵循同一个协议时,Self 会自动变成对应地类型:
class PDFDocument: Document {
// duplicate() 返回的是 PDFDocument,不是 Document
}
class WordDocument: Document {
// duplicate() 返回的是 WordDocument,不是 Document
}
Self.self:类型的元类型
最后说说 Self.self
,这个是最让人懵的。它表示的是类型本身的元类型(metatype)。
啥是元类型?简单说就是"类型的类型"。如果说 String
是字符串类型,那么 String.Type
就是字符串类型的类型。
来看个实际例子:
protocol Registrable {
staticfunc register()
}
extension Registrable {
staticfunc register() {
// Self.self 获取的是具体类型的元类型
print("正在注册 \(Self.self)")
}
}
class UserService: Registrable {}
class PaymentService: Registrable {}
// 调用静态方法
UserService.register() // 打印: 正在注册 UserService
PaymentService.register() // 打印: 正在注册 PaymentService
在实际开发中,Self.self 经常用在需要传递类型信息的场景:
// 泛型函数,需要知道具体类型
func createInstance(of type: T.Type, from json: String) -> T? {
// 使用传入的类型信息来解码 JSON
let data = json.data(using: .utf8)!
returntry? JSONDecoder().decode(type, from: data)
}
// 在协议扩展中使用
protocol JSONDecodable: Decodable {
staticfunc decode(from json: String) -> Self?
}
extension JSONDecodable {
staticfunc decode(from json: String) -> Self? {
// 这里的 Self.self 就是调用者的具体类型
return createInstance(of: Self.self, from: json)
}
}
实战技巧:什么时候用哪个?
总结一下使用场景:
用 self 的情况:
用 Self 的情况:
用 Self.self 的情况:
容易踩的坑
最后分享几个常见的坑:
1. 在 class 中使用 Self 作返回值
class Animal {
// 错误:class 中不能直接返回 Self
// func clone() -> Self { ... }
// 正确:需要用 required init
func clone() -> Self {
return type(of: self).init()
}
required init() {}
}
2. 忘记 weak self
class ViewController {
func loadData() {
APIClient.request { data in
// 可能造成循环引用
self.updateUI(with: data)
// 使用 weak self
// [weak self] in
// self?.updateUI(with: data)
}
}
}
3. Self 和具体类型名混用
protocol Comparable {
// 不够灵活
func isEqual(to other: MyClass) -> Bool
// 使用 Self 更通用
func isEqual(to other: Self) -> Bool
}
写在最后
其实这三个概念并不难理解,关键是要在实践中多用。我的建议是:
先用对 :确保你知道在什么场景该用哪个 再用好 :理解背后的原理,写出更优雅的代码 常复习 :即使是老手也会忘记,定期温习很重要
说真的,我现在写代码遇到这三个概念,基本不用思考就知道该用哪个了。但这也是踩了无数坑之后得出的经验啊!
下次再遇到 self、Self 和 Self.self,你应该不会再迷糊了吧?如果还是搞不清楚,收藏这篇文章,忘了就回来看看~(我自己也经常翻看自己写的文章来复习,哈哈)
你在使用这三个概念时遇到过什么坑吗?欢迎在评论区分享你的经历!
这里每天分享一个 iOS 的新知识,快来关注我吧