程序设计和人类哲学所面临的同一个很重大的课题就是解决 "我是谁" 这个问题。在哲学里,这个问题属于自我认知的范畴,而在程序设计时,这个问题涉及到自省 (Introspection)。
向一个对象发出询问,以确定它是不是属于某个类,这种操作就称为自省。在 Objective-C 中因为 id
这样的可以指向任意对象的指针的存在 (其实严格来说 Objective-C 的指针的类型都是可以任意指向和转换的,它们只不过是帮助编译器理解你的代码而已),我们经常需要向一个对象询问它是不是属于某个类。常用的方法有下面两类:
[obj1 isKindOfClass:[ClassA class]];
[obj2 isMemberOfClass:[ClassB class]];
-isKindOfClass:
判断 obj1
是否是 ClassA
或者其子类的实例对象;而 isMemberOfClass:
则对 obj2
做出判断,当且仅当 obj2
的类型为 ClassB
时返回为真。
这两个方法是 NSObject
的方法,所以我们在 Swift 中如果写的是 NSObject
的子类的话,直接使用这两个方法是没有任何问题的:
class ClassA: NSObject { }
class ClassB: ClassA { }
let obj1: NSObject = ClassB()
let obj2: NSObject = ClassB()
obj1.isKindOfClass(ClassA.self) // true
obj2.isMemberOfClass(ClassA.self) // false
关于 .self
的用法,我们在 .self 和 AnyClass 一节里已经有所提及,这里就不再重复了。
在 Objective-C 中我们几乎所有的类都会是 NSObject
的子类,而在 Swift 的世界中,处于性能考虑,只要有可能,我们应该更倾向于选择那些非 NSObject
子类的 Swift 原生类型。对于那些不是 NSObject
的类,我们应该怎么确定其类型呢?
首先需要明确的一点是,我们为什么需要在运行时去确定类型。因为有泛型支持,Swift 对类型的推断和记录是完备的。因此在绝大多数情况下,我们使用的 Swift 类型都应该是在编译期间就确定的。如果在你写的代码中经常需要检查和确定 AnyObject
到底是什么类的话,几乎就意味着你的代码设计出了问题 (或者你正在写一些充满各种 "小技巧" 的代码)。虽然没有太多的意义,但是我们还是可以做这件事情:
class ClassA { }
class ClassB: ClassA { }
let obj1: AnyObject = ClassB()
let obj2: AnyObject = ClassB()
obj1.isKindOfClass(ClassA.self) // true
obj2.isMemberOfClass(ClassA.self) // false
在 Swift 中对于 AnyObject
使用最多的地方应该就是原来那些返回 id
的 Cocoa API 了。
为了快速确定类型,Swift 提供了一个简洁的写法:对于一个不确定的类型,我们现在可以使用 is
来进行判断。is
在功能上相当于原来的 isKindOfClass
,可以检查一个对象是否属于某类型或其子类型。is
和原来的区别主要在于亮点,首先它不仅可以用于 class
类型上,也可以对 Swift 的其他像是 struct
或 enum
类型进行判断。使用起来是这个样子的:
class ClassA { }
class ClassB: ClassA { }
let obj: AnyObject = ClassB()
if (obj is ClassA) {
print("属于 ClassA")
}
if (obj is ClassB) {
print("属于 ClassB")
}
另外,编译器将对这种检查进行必要性的判断:如果编译器能够唯一确定类型,那么 is
的判断就没有必要,编译器将会抛出一个警告,来提示你并没有转换的必要。
let string = "String"
if string is String {
// Do something
}
// 'is' test is always true