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。