iPhone內省機制是本文要介紹的內容,從評估繼承關係、方法實現和協議遵循、對象的比較等方面來詳細的學習iPhone內省機制,我們先來看詳細內容。
內省Introspection)是物件導向語言和環境的一個強大特性,Objective-C和Cocoa在這個方面尤其的豐富。內省是對象揭示自己作為一個運行時對象的詳細資料的一種能力。這些詳細資料包括對象在繼承樹上的位置,對象是否遵循特定的協議,以及是否可以響應特定的訊息。NSObject協議和類定義了很多內省方法,用於查詢運行時資訊,以便根據對象的特徵進行識別。
明智地使用內省可以使物件導向的程式更加高效和強壯。它有助於避免錯誤地進行訊息派發、錯誤地假設對象相等、以及類似的問題。下面的部分將介紹如何在代碼中有效地使用NSObject的內省方法。
評估繼承關係
一旦您知道一個對象屬於什麼類,就可能已經相當瞭解這個對象了。您可以知道它具有什麼能力、哪些屬性、以及可以響應哪些訊息。即使在內省之後不能瞭解對象所屬的類,也可以知道該對象不能響應特定的訊息。
NSObject協議聲明了幾個方法,用於確定對象在類層次中的位置。這些方法在不同粒度上進行操作,比如class和superclass執行個體方法分別返回代表類和超類的Class對象。使用這些方法需要將一個Class對象和另一個進行對比。列表2-7給出了一個簡單可能是沒有價值)的用法執行個體。
使用類和超類的方法
- // ...
- while ( id anObject = [objectEnumerator nextObject] ) {
- if ( [self class] == [anObject superclass] ) {
- // do something appropriate...
- }
- }
請注意:有些時候您需要通過class或superclass方法得到正確的類訊息接收者。
更加常見的是檢查對象類的從屬關係,這種情況下您需要向該對象發送isKindOfClass:或isMemberOfClass:訊息。前一個方法返回接收者是否為給定類或其繼承類的執行個體,isMemberOfClass:訊息則告訴您接收者是否為指定類的執行個體。isKindOfClass: 方法通常更有用,因為通過它可以知道是否可以向該對象發送一系列訊息。考慮列表2-8中的代碼片斷:
使用isKindOfClass:方法
- if ([item isKindOfClass:[NSData class]]) {
- const unsigned char *bytes = [item bytes];
- unsigned int length = [item length];
- // ...
- }
確定tem對象是NSData類的繼承類的執行個體之後,代碼就知道可以向它發送NSData的bytes和length訊息。假定item是NSMutableData類的一個執行個體,則isKindOfClass:和isMemberOfClass:之間的差別就變得更加明顯。如果您調用的是isMemberOfClass:,而不是isKindOfClass:,條件控制塊中的代碼將永遠不會被執行,因為item並不是NSData類的執行個體,而是其子類NSMutableData的執行個體。
方法實現和協議遵循
NSObject還有兩個功能更加強大的內省方法,即respondsToSelector:和conformsToProtocol:。這兩個方法分別告訴您一個對象是否實現特定的方法,以及是否遵循指定的正式協議即該對象是否採納了該協議,且實現了該協議的所有方法)。
在代碼中,您可以在類似的情況下使用這些方法。通過這些方法,您可以在將訊息或訊息集合發送給某些潛在的匿名對象之前,確定它們是否可以正確地進行響應。在發送訊息之前進行檢查可以避免由不能識別的選取器引起的運行時例外。
在實現非正式協議這種協議是委託技術的基礎)時,Application Kit就是在調用委託方法之前檢查委派物件是否實現該方法通過respondsToSelector:方法)。
顯示了如何在代碼中使用respondsToSelector:方法。
使用respondsToSelector:方法
- - (void)doCommandBySelector:(SEL)aSelector {
- if ([self respondsToSelector:aSelector]) {
- [self performSelector:aSelector withObject:nil];
- } else {
- [_client doCommandBySelector:aSelector];
- }
- }
顯示如何在代碼中使用conformsToProtocol:方法:
使用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;
- }
對象的比較
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。
小結:深度解析iPhone內省機制的內容介紹完了,希望本文對你有所協助!