Objective-C的Introspection(内省)

1,221 阅读3分钟

Introspection 官方文档

Introspection(内省)

  • 内省是面向对象语言和环境的一个强大功能,Objective-C和Cocoa的内省也不例外。
  • 内省是指对象在运行时将其自身细节泄露为对象的能力。 这些细节包括对象在继承树中的位置,它是否符合特定的协议,以及它是否响应某个特定的消息。以及它是否响应某一消息。

NSObject 协议和类定义了许多内省方法, 可用于查询运行时以描述对象。

  1. class和superclass方法
  2. isKindOfClass:和isMemberOfClass:
  3. respondsToSelector:
  4. conformsToProtocol:
  5. isEqual:

评估继承关系

  • 使用classsuperclass方法
// ...
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。