[英]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.