在iOS代码中经常用到:isKindOfClass:Class一类的方法,但是对它没有一个了解,在网上搜索了一些内容对它进行了介绍并拓展,这些属于OC的特性之一内省。
内省(Introspection)是面向对象语言和环境的一个强大特性,Objective-C和Cocoa在这个方面尤其的丰富。内省是对象揭示自己作为一个运行时对象的详细信息的一种能力。这些详细信息包括对象在继承树上的位置,对象是否遵循特定的协议,以及是否可以响应特定的消息。NSObject协议和类定义了很多内省方法,用于查询运行时信息,以便根据对象的特征进行识别。
明智地使用内省可以使面向对象的程序更加高效和强壮。它有助于避免错误地进行消息派发、错误地假设对象相等、以及类似的问题。
下面的部分将介绍如何在代码中有效地使用NSObject的内省方法。
1、isKindOfClass:Class
检查对象是否是那个类或者其继承类实例化的对象
2、isMemberOfClass:Class
检查对象是否是那个类但不包括继承类而实例化的对象
示例:
- if ([item isKindOfClass:[NSData class]]) {
- const unsigned char *bytes = [item bytes];
- unsigned int length = [item length];
- // ...
- }
如果item是NSMutableData类实例化的对象,而该类是NSData类的子类,那么[item isKindOfClass:[NSData class]]的值也是TRUE,而[item isMemberOfClass:[NSData class]]的值则为False。
如果item是NSData类实例化的对象,那么[item isMemberOfClass:[NSData class]]的值则为TRUE。
3、respondToSelector:selector
检查对象是否包含这个方法
- - (void)doCommandBySelector:(SEL)aSelector {
- if ([self respondsToSelector:aSelector]) {
- [self performSelector:aSelector withObject:nil];
- } else {
- [_client doCommandBySelector:aSelector];
- }
- }
4、conformsToProtocol:protocol
检查对象是否符合协议,是否实现了协议中所有的必选方法。
- // ...
- if (!([((id)testObject) conformsToProtocol:@protocol(NSMenuItem)])) {
- NSLog(@"Custom MenuItem, '%@', not loaded; it must conform to the
- 'NSMenuItem' protocol.\n", [testObject class]);
- [testObject release];
- testObject = nil;
- }
对象的比较
hash和isEqual:方法虽然不是严格的内省方法,但是可以发挥类似的作用,是进行对象的识别和比较时不可或缺的运行时工具。它们并不向运行环境查询对象信息,而是依赖于具体类的比较逻辑。
hash和isEqual:方法都在NSObject协议中声明,且彼此关系紧密。实现hash方法必须返回一个整型数,作为哈希表结构中的表地址。两个对象相等(isEqual:方法的判断结果)意味着它们有相同的哈希值。如果您的对象可能被包含在象NSSet这样的集合中,则需要定义hash方法,并确保该方法在两个对象相等的时候返回相同的哈希值。NSObject类中缺省的isEqual:实现只是简单地检查指针是否相等。
isEqual:的使用相当直接,它将消息的接收者和通过参数传入的对象进行比较。对象的比较常常可以在运行时决定应该对对象做些什么。如列表2-11所示,您可以通过isEqual:来确定是否执行某一个动作。在这个例子中,动作是指保存被修改了的预置信息。
使用isEqual:方法
- - (void)saveDefaults {
- NSDictionary *prefs = [self preferences];
- if (![origValues isEqual:prefs])
- [Preferences savePreferencesToDefaults:prefs];
- }
如果您正在创建子类,则可能需要重载isEqual:方法,以进一步检查对象是否相等。子类可能定义额外的属性,当两个实例被认为相等时,属性的值必须相同。举例来说,假定您创建一个名为MyWidget的NSObject子类,类中包含两个实例变量:name和data。当MyWidget的两个实例被认为是相等时,这些变量必须具有相同的值。列表2-12显示如何在MyWidget类中实现isEqual:方法。
重载isEqual:方法
- - (BOOL)isEqual:(id)other {
- if (other == self)
- return YES;
- if (!other || ![other isKindOfClass:[self class]])
- return NO;
- return [self isEqualToWidget:other];
- }
- - (BOOL)isEqualToWidget:(MyWidget *)aWidget {
- if (self == aWidget)
- return YES;
- if (![(id)[self name] isEqual:[aWidget name]])
- return NO;
- if (![[self data] isEqualToData:[aWidget data]])
- return NO;
- return YES;
- }
isEqual:方法首先检查指针的等同性,然后是类的等同性,最后调用对象的比较器进行比较。比较器的名称指示出参与比较的对象的类名称。这种类型的比较器对传入的对象进行强制类型检查,是Cocoa中常见的约定,NSString的isEqualToString:和NSTimeZone的isEqualToTimeZone:就是两个这样的例子。特定类的比较器(在这个例子中是isEqualToWidget:)负责执行name和data变量的等同性。
在Cocoa框架的所有isEqualToType:方法中,nil都不是正当的参数,这些方法的实现在接收到nil参数时会抛出例外。然而为了向后兼容,Cocoa框架中的isEqual:方法可以接收nil值,在这种情况下返回NO。