如何在Swift中获取对象的类名:性能与最佳实践

面向初学者的在线Python训练器

轻松学习Python,无需理论负担。在浏览器中直接解决带自动检查的实践任务,无需安装任何东西。

开始课程

为什么Swift开发者需要将类名作为字符串?

\n\n

想象一下这个场景:你正在为一个复杂的应用程序开发日志记录功能。每个事件不仅应包含消息,还应包含发生该事件的类名。或者你正在构建一个通用的JSON解析器,它需要根据字符串键确定模型类型。又或者你在调试循环引用,并希望动态输出内存中所有对象及其类名。

\n\n

在所有上述情况下,你都需要做同一件事:在Swift中以字符串形式获取对象的类名。看起来很简单,对吧?但事情并非如此简单。与Objective-C不同,Swift没有一个在所有情况下都能完美工作的单一“魔法”方法。此外,不同的方法性能不同,有些还会产生意想不到的结果。

\n\n

在本文中,我们不仅会给出现成的答案,还会分析所有在Swift中获取类名的方法,比较它们的速度,并展示何时应该使用哪种方法。你将学习如何避免潜在陷阱,并编写在生产环境中不会出错的代码。

\n\n

经典方法:String(describing: type(of:))

\n\n

关于这个话题,Stack Overflow上最流行的答案是使用type(of:)和String(describing:)的组合。让我们分析一下它是如何工作的。

\n\n
class Car {\n    var model: String\n    init(model: String) {\n        self.model = model\n    }\n}\n\nlet myCar = Car(model: "Tesla Model 3")\nlet className = String(describing: type(of: myCar))\nprint(className) // "Car"
\n\n

这种方法确实简单直观。type(of:)返回对象的元类型,而String(describing:)将其转换为字符串。但有一个问题:它不能正确工作于可选类型和协议。

\n\n

可选类型的问题

\n\n
let optionalCar: Car? = Car(model: "BMW")\nlet optionalName = String(describing: type(of: optionalCar))\nprint(optionalName) // "Optional<Car>" — 这不是你期望的结果!
\n\n

你得到的是"Optional<Car>"而不是"Car"。这是因为type(of:)返回的是值本身的类型,而不是其内容的类型。对于可选类型,它是Optional<Car>。

\n\n

协议和继承的问题

\n\n
protocol Drivable { }\nextension Car: Drivable { }\n\nlet drivableObject: Drivable = Car(model: "Audi")\nlet protocolName = String(describing: type(of: drivableObject))\nprint(protocolName) // "Car" — 这次运气好,但并非总是如此
\n\n

对于协议,结果可能不可预测,尤其是在使用协议组合或Any时。结论:这种方法适用于简单情况,但不适用于包含可选类型和复杂类型的生产代码。

\n\n

Objective-C遗产:NSStringFromClass

\n\n

如果你来自Objective-C,你肯定记得NSStringFromClass([self class])。在Swift中这也有效,但有一些注意事项:

\n\n
import Foundation\n\nclass Motorcycle {\n    var brand: String\n    init(brand: String) {\n        self.brand = brand\n    }\n}\n\nlet bike = Motorcycle(brand: "Harley")\nlet objcClassName = NSStringFromClass(type(of: bike))\nprint(objcClassName) // "_TtC11YourModule11Motorcycle" — 修饰后的名称!
\n\n

在Swift中,类具有“修饰过的”(mangled)名称。你得到的是类似_TtC11YourModule11Motorcycle的东西,而不是"Motorcycle"。要获得纯净的名称,你需要手动“解析”它,或者使用String(describing:)。

\n\n

然而,有一个重要的优点:NSStringFromClass适用于任何对象,包括可选类型,并且在nil上不会崩溃。但为了代码的简洁性,最好不要直接使用它。

\n\n

什么时候NSStringFromClass仍然必要?

\n
    \n
  • 当与Obj

博客

书籍推荐