• Swift中self、Self、Self.self 这三个关键字的用法区别
  • 发布于 1周前
  • 39 热度
    0 评论
  • APAC
  • 18 粉丝 40 篇博客
  •   
前言
昨天在 Code Review 的时候,一个同事突然问我:"这里为什么要用 Self.self 而不是 self?"虽然天天在写这些代码,但要清清楚楚地解释这几个概念的区别,还真有点费劲。你有没有过这种经历?写代码的时候,self、Self、Self.self 这几个家伙总是让人傻傻分不清楚。即使是写了好几年 Swift 的老鸟,有时候也会停下来想想:"这里该用哪个来着?"
今天就来好好聊聊这几个看起来很像、但实际上完全不同的概念。

self:指向实例本身
先说最常见的 self(小写的 s)。这个最简单,它就是指向当前实例的引用。
class Person {
    var name: String
    // 堆代码 duidaima.com
    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<T: Decodable>(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,你应该不会再迷糊了吧?如果还是搞不清楚,收藏这篇文章,忘了就回来看看~(我自己也经常翻看自己写的文章来复习,哈哈)!你在使用这三个概念时遇到过什么坑吗?欢迎在评论区分享你的经历!
用户评论