Introspection(内省)
- 内省是面向对象语言和环境的一个强大功能,Objective-C和Cocoa的内省也不例外。
- 内省是指对象在运行时将其自身细节泄露为对象的能力。 这些细节包括对象在继承树中的位置,它是否符合特定的协议,以及它是否响应某个特定的消息。以及它是否响应某一消息。
NSObject 协议和类定义了许多内省方法, 可用于查询运行时以描述对象。
- class和superclass方法
- isKindOfClass:和isMemberOfClass:
- respondsToSelector:
- conformsToProtocol:
- isEqual:
评估继承关系
- 使用
class
和superclass
方法
// ...
while ( id anObject = [objectEnumerator nextObject] ) {
if ( [self class] == [anObject superclass] ) {
// do something appropriate...
}
}
注意:有时您使用classor superclass方法来获取class消息的适当接收者。
-
isKindOfClass:
与isMemberOfClass:
-
相同点:都是NSObject中比较Class的方法。
-
不同点:
isKindOfClass:
用来判断某个对象是否属于 某个类,或者是属于该类 继承的任何类的实例类。
isMemberOfClass:
用来判断某个对象是否为 指定类 的实例。 不能检测任何的类都是基于NSObject类这一事实,而isKindOfClass可以。
-
首先创建个JTBody类基于NSObject,然后创建个JTFoot类基于JTBody类
- (void) test {
JTFoot *foot = [[JTFoot alloc] init];
if ([foot isKindOfClass:[JTBody class]]) {
NSLog(@"foot isKindOfClass JTBody");
}
if ([foot isMemberOfClass:[JTFoot class]]) {
NSLog(@"foot isMemberOfClass JTFoot");
}
if ([foot isMemberOfClass:[JTBody class]]) {
NSLog(@"foot isMemberOfClass JTBody");
}
}
打印也验证了上边对两者的论述:
IntroductionDemo[5380:2729984] foot isKindOfClass JTBody
IntroductionDemo[5380:2729984] foot isMemberOfClass JTFoot
方法的实现和协议一致性
respondsToSelector:
检查一个对象是否实现了某种方法
conformsToProtocol:
检查一个对象是否符合指定的形式协议 使用respondsToSelector:
- (void)doCommandBySelector:(SEL)aSelector {
if([selfrespondsToSelector:aSelector]){
[self performSelector:aSelector withObject:nil];
} else {
[_client doCommandBySelector:aSelector];
}
}
使用conformsToProtocol:
// ...
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;
}
对象比较
- isEqual:
虽然他们不是严格的内省方法,但是 hash 和 isEqual:方法履行类似的作用。它们是用于识别和比较对象的不可或缺的运行时工具。但不是查询运行时获取关于对象的信息,而是依赖于特定于类的比较逻辑。
- (void)saveDefaults {
NSDictionary *prefs = [self preferences];
if (![origValues isEqual:prefs])
[Preferences savePreferencesToDefaults:prefs];
}
如果您正在创建一个子类,您可能需要重写
isEqual:
以添加对相等点的进一步检查。子类可能会定义一个额外的属性,在两个实例中它们必须是相同的值才能被视为相等。例如,假设你创建一个包含两个实例变量name 与data的NSObject子类MyWidget。这两者必须是两个相同的值MyWidget才能被视为相等。
重写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中是一个常见的约定; 类的isEqualToString:
方法和NSString类的isEqualToTimeZone:
方法NSTimeZone只是两个例子。类特定的比较器- isEqualToWidget:
在这种情况下 - 执行名称和数据相等性的检查。
在Cocoa框架的所有
isEqualToType:
方法中,nil不是一个有效的参数,并且这些方法的实现可能会在接收到一个异常时引发异常nil。但是,为了向后兼容,isEqual:
Cocoa框架的方法确实接受nil并返回NO。