北屋教程网

专注编程知识分享,从入门到精通的编程学习平台

Swift 这三个关键字,连苹果工程师都经常搞错

这里每天分享一个 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
}

写在最后

其实这三个概念并不难理解,关键是要在实践中多用。我的建议是:

  1. 先用对 :确保你知道在什么场景该用哪个
  2. 再用好 :理解背后的原理,写出更优雅的代码
  3. 常复习 :即使是老手也会忘记,定期温习很重要

说真的,我现在写代码遇到这三个概念,基本不用思考就知道该用哪个了。但这也是踩了无数坑之后得出的经验啊!

下次再遇到 self、Self 和 Self.self,你应该不会再迷糊了吧?如果还是搞不清楚,收藏这篇文章,忘了就回来看看~(我自己也经常翻看自己写的文章来复习,哈哈)

你在使用这三个概念时遇到过什么坑吗?欢迎在评论区分享你的经历!

这里每天分享一个 iOS 的新知识,快来关注我吧

控制面板
您好,欢迎到访网站!
  查看权限
网站分类
最新留言