[英]Why can -respondsToSelector: instance method be used on class name or class object?
在Objective C的編程,4e,第9章,程序9.3中:
#import "Square.h"
int main (int argc, char * argv[])
{
@autoreleasepool {
Square *mySquare = [[Square alloc] init];
...
// respondsTo:
if ( [mySquare respondsToSelector: @selector (setSide:)] == YES )
NSLog (@"mySquare responds to setSide: method");
...
if ( [Square respondsToSelector: @selector (alloc)] == YES )
NSLog (@"Square class responds to alloc method");
...
}
return 0;
}
Q1:
由於-respondsToSelector:
是實例方法,而不是類方法,為什么可以直接在Square
類上使用它?
Q2:
該書說您可以在此處使用Square
而不是[Square class]
。 它只是一個特殊的捷徑,還是背后有任何機制?
任何幫助將非常感激! 提前致謝!
Q1:
簡單的答案是,除了類方法之外,您還可以在類對象上調用根類的任何實例方法(無論您的類的根類是什么;在本例中為NSObject
)。
更為復雜的答案是,類對象是元類的實例。 實例方法是實例上在類中定義的方法; 類方法是類對象上的方法,這些方法在元類中定義。 每個類都有其自己的元類。 元類的繼承遵循其類的繼承。 即NSString
的元類繼承自NSObject
的元類。 最終,根類的元類繼承自根類。 即NSObject
的元類繼承自NSObject
。 這就是為什么所有NSObject的實例方法都可用於類對象的原因。
Q2:
[Square class]
調用類方法+class
(這與-class
無關)。 +class
本質上是一個標識方法,它僅返回調用它的東西(就像-self
一樣); 也就是說,如果foo
是指向類對象的指針,則[foo class]
與foo
相同。
所以+class
似乎毫無用處; 我們為什么用它? 這是因為在Objective-C語言的語法中,類名不是有效的表達式(與Smalltalk不同)。 因此,您不能說id bar = Square;
; 那不會編譯。 作為語法的一種特殊情況,在消息調用表達式中允許使用類名代替接收者,並將消息發送到類對象。 即[Square something]
。 因此,如果您想在任何其他表達式上下文中使用class對象,我們可以通過調用+class
的標識方法,以一種繞行方式進行操作 即[Square class]
是可以在任何表達式上下文中使用的表達式( [Square self]
也可以使用,但是我們習慣上使用[Square class]
,這很不幸,因為它與-class
混淆了); 我們本來希望只使用Square
,但由於語言原因而不能。
在您的情況下,它已經是消息調用表達式中的接收者,因此不必執行[Square class]
; Square
已經在那里工作了。
從Objective-C編程語言 , 對象,類和消息傳遞中 ,
所有對象,類和實例都需要與運行時系統的接口。 類對象和實例都應該能夠對它們的功能進行自省,並能夠報告它們在繼承層次結構中的位置。 提供此接口是NSObject類的省。
這樣一來,不必執行兩次NSObject方法(一次為實例提供運行時接口,再為類對象復制該接口),就可以對類對象進行特殊分配以執行根類中定義的實例方法 。 當類對象收到無法使用類方法響應的消息時,運行時系統將確定是否存在可以響應的根實例方法。 類對象可以執行的唯一實例方法是在根類中定義的實例方法,並且僅當沒有類方法可以執行此工作時才可以執行 。
在這種情況下, NSObject
是根類。 由於NSObject
實例均符合定義了-respondsToSelector:
NSObject protocol
,因此大多數類對象應能夠執行-respondsToSelector:
// Q1:
由於-respondsToSelector:是實例方法,而不是類方法,為什么可以直接在Square類上使用它?//
您似乎有一個想法,即不能從實例方法中調用類方法(反之亦然)。 相反,這似乎是方法-respondsToSelector的意圖,很可能是通過使用-class方法獲取發送方的類,然后查詢該類是否對選擇器作出響應並返回YES或NO。 在一個更加本地化的示例中,請考慮以下內容:
-(void)someInstanceMethod{
[MyCurrentClass doClassMethod]; //basic equivalent of [self doClassMethid];
}
如果MyCurrentClass全部分配和初始化,則在Objective-C中完全有效。
//第二季度:
這本書說您可以在這里使用Square而不是[Square class]。 它只是一個特殊的捷徑,還是背后有任何機制?//
將-class發送到Class完全是多余的! 這沒有什么意義,只是多余的代碼。 -class只是查詢接收者的類,無論它是實例還是Class對象。
當前,Objective C運行時將一個類實現為某個其他類的實例對象。 因此,一個類將響應某些實例方法。
真正的實現,直接來自NSObject.m,是這樣的:
- (BOOL)respondsToSelector:(SEL)aSelector {
PF_HELLO("")
return class_respondsToSelector( isa, aSelector );
}
現在,我不知道為什么有PF_HELLO("")
,但是正如您所看到的,它實際上是在運行時PF_HELLO("")
CLASS“嘿,您是否有一個用於此isa的方法,稱為aSelector?”
並且,在Objective-C中,類方法ALSO屬於實例,但是優先級較低(與類方法同名的實例方法在類方法之前被調用)。
Objective-C動態鍵入的另一個方面是, id
類型實際上聲明如下:
typedef struct objc_class *Class;
typedef struct objc_object {
Class isa;
} *id;
因此,您的實例對象實際上是一個Class指針。 這意味着您的-respondsToSelector消息也將轉到實例類型的Class。 在您的情況下,這意味着-respondsToSelector將轉到objc_class FIRST。
現在在一個測試案例中(直接來自libFoundation),我的答案將像這樣總結:
Test *tst = [Test new];
fail_unless([tst respondsToSelector:@selector(testInstanceMethod)], "-[Test respondsToSelector:] returned NO for a valid instance method (testInstanceMethod).");
fail_if([tst respondsToSelector:@selector(testClassMethod)], "-[Test respondsToSelector:] returned YES for a class method (testInstanceMethod).");
fail_unless([Test respondsToSelector:@selector(testClassMethod)], "+[Test respondsToSelector:] returned NO for a valid class method (testClassMethod).");
fail_if([Test respondsToSelector:@selector(testInstanceMethod)], "+[Test respondsToSelector:] returned YES for an instance method (testInstanceMethod).");
fail_unless([tst respondsToSelector:@selector(init)], "-[Test respondsToSelector:] returned NO for an inherited instance method (-[NSObject init].");
fail_unless([Test respondsToSelector:@selector(alloc)], "+[Test respondsToSelector:] returned NO for an inherited class method (+[NSObject alloc]).");
[tst release];
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.