繁体   English   中英

为什么可以在类名或类对象上使用-respondsToSelector:实例方法?

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

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM