简体   繁体   English

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

[英]Why can -respondsToSelector: instance method be used on class name or class object?

In Programming in Objective C, 4e, Chapter 9, Program 9.3: 在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: Q1:

Since -respondsToSelector: is an instance method, not a class method, why would it be possible to use it on Square class directly? 由于-respondsToSelector:是实例方法,而不是类方法,为什么可以直接在Square类上使用它?

Q2: Q2:

The book says you can use Square here instead of [Square class] . 该书说您可以在此处使用Square而不是[Square class] Is it only a exceptional shortcut, or is there any mechanism behind this? 它只是一个特殊的捷径,还是背后有任何机制?

Any help would be really appreciated! 任何帮助将非常感激! Thanks in advance! 提前致谢!

Q1: Q1:

The simple answer is that, in addition to class methods, you can call any instances method of the root class (whatever the root class of your class is; in this case, NSObject ) on a class object. 简单的答案是,除了类方法之外,您还可以在类对象上调用根类的任何实例方法(无论您的类的根类是什么;在本例中为NSObject )。

The more complicated answer is that class objects are instances of metaclasses. 更为复杂的答案是,类对象是元类的实例。 Whereas instance methods are methods on an instance, which are defined in the class; 实例方法是实例上在类中定义的方法; class methods are methods on the class object, which are defined in the metaclass. 类方法是类对象上的方法,这些方法在元类中定义。 Each class has its own metaclass. 每个类都有其自己的元类。 The inheritance of metaclasses follows that of their classes; 元类的继承遵循其类的继承。 ie NSString 's metaclass inherits from NSObject 's metaclass. NSString的元类继承自NSObject的元类。 Ultimately, the root class's metaclass inherits from the root class; 最终,根类的元类继承自根类。 ie NSObject 's metaclass inherits from NSObject . NSObject的元类继承自NSObject That is why all of NSObject's instance methods are available to class objects. 这就是为什么所有NSObject的实例方法都可用于类对象的原因。

Q2: Q2:

[Square class] calls the class method +class (this is unrelated to -class ). [Square class]调用类方法+class (这与-class无关)。 +class is essentially an identity method that simply returns the thing called on it (just like -self ); +class本质上是一个标识方法,它仅返回调用它的东西(就像-self一样); ie if foo is a pointer to a class object, then [foo class] is the same as foo . 也就是说,如果foo是指向类对象的指针,则[foo class]foo相同。

So +class seems pretty useless; 所以+class似乎毫无用处; why do we use it? 我们为什么用它? That is because in the grammar of the Objective-C language, a class name is not a valid expression (unlike Smalltalk). 这是因为在Objective-C语言的语法中,类名不是有效的表达式(与Smalltalk不同)。 So you cannot say id bar = Square; 因此,您不能说id bar = Square; ; ; that would not compile. 那不会编译。 As a special case in the grammar, a class name is allowed in place of the receiver in a message call expression, and the message is sent to the class object; 作为语法的一种特殊情况,在消息调用表达式中允许使用类名代替接收者,并将消息发送到类对象。 ie [Square something] . [Square something] So if you want to use the class object in any other expression context, we do this in a roundabout way by calling an identity method like +class ; 因此,如果您想在任何其他表达式上下文中使用class对象,我们可以通过调用+class的标识方法,以一种绕行方式进行操作 ie [Square class] is an expression that can be used in any expression context ( [Square self] would also work, but we use [Square class] by convention, which is unfortunate, since it is confused with -class ); [Square class]是可以在任何表达式上下文中使用的表达式( [Square self]也可以使用,但是我们习惯上使用[Square class] ,这很不幸,因为它与-class混淆了); we would have liked to just use Square , but can't due to the language. 我们本来希望只使用Square ,但由于语言原因而不能。

In your case, it is already the receiver in a message call expression, so it is unnecessary to do [Square class] ; 在您的情况下,它已经是消息调用表达式中的接收者,因此不必执行[Square class] Square already works there. Square已经在那里工作了。

From The Objective-C Programming Language , Objects, class, and Messaging , Objective-C编程语言对象,类和消息传递中

All objects, classes and instances alike, need an interface to the runtime system. 所有对象,类和实例都需要与运行时系统的接口。 Both class objects and instances should be able to introspect about their abilities and to report their place in the inheritance hierarchy. 类对象和实例都应该能够对它们的功能进行自省,并能够报告它们在继承层次结构中的位置。 It's the province of the NSObject class to provide this interface. 提供此接口是NSObject类的省。

So that NSObject methods don't have to be implemented twice—once to provide a runtime interface for instances and again to duplicate that interface for class objects— class objects are given special dispensation to perform instance methods defined in the root class . 这样一来,不必执行两次NSObject方法(一次为实例提供运行时接口,再为类对象复制该接口),就可以对类对象进行特殊分配以执行根类中定义的实例方法 When a class object receives a message that it can't respond to with a class method, the runtime system determines whether there's a root instance method that can respond. 当类对象收到无法使用类方法响应的消息时,运行时系统将确定是否存在可以响应的根实例方法。 The only instance methods that a class object can perform are those defined in the root class , and only if there's no class method that can do the job . 类对象可以执行的唯一实例方法是在根类中定义的实例方法,并且仅当没有类方法可以执行此工作时才可以执行

In this case, NSObject is the root class. 在这种情况下, NSObject是根类。 As NSObject instances all comply with NSObject protocol , where -respondsToSelector: is defined, most class objects should be able to perform -respondsToSelector: . 由于NSObject实例均符合定义了-respondsToSelector: NSObject protocol ,因此大多数类对象应能够执行-respondsToSelector:

//Q1: // Q1:

Since -respondsToSelector: is an instance method, not a class method, why would it be possible to use it on Square class directly?// 由于-respondsToSelector:是实例方法,而不是类方法,为什么可以直接在Square类上使用它?//

You seem to have this notion that class methods cannot be called from instance methods (and vice versa). 您似乎有一个想法,即不能从实例方法中调用类方法(反之亦然)。 On the contrary, it would seem to be the intent of the method -respondsToSelector to do so, most likely by getting the class of the sender with the -class method, then querying if the class responds to the selector and returning YES or NO. 相反,这似乎是方法-respondsToSelector的意图,很可能是通过使用-class方法获取发送方的类,然后查询该类是否对选择器作出响应并返回YES或NO。 In a more localized example, consider the following: 在一个更加本地化的示例中,请考虑以下内容:

-(void)someInstanceMethod{
     [MyCurrentClass doClassMethod]; //basic equivalent of [self doClassMethid];
}

Is perfectly valid in Objective-C, provided MyCurrentClass is all alloc'd and init'ed. 如果MyCurrentClass全部分配和初始化,则在Objective-C中完全有效。

//Q2: //第二季度:

The book says you can use Square here instead of [Square class]. 这本书说您可以在这里使用Square而不是[Square class]。 Is it only a exceptional shortcut, or is there any mechanism behind this?// 它只是一个特殊的捷径,还是背后有任何机制?//

It is completely redundant to send -class to a Class! 将-class发送到Class完全是多余的! It makes little sense, and is just extra unnecessary code. 这没有什么意义,只是多余的代码。 -class just queries for the reciever's class, no matter if it is an instance or Class object. -class只是查询接收者的类,无论它是实例还是Class对象。

The Objective C run-time currently implements a class as an instance object of some other class. 当前,Objective C运行时将一个类实现为某个其他类的实例对象。 Thus a class will response to certain instance methods. 因此,一个类将响应某些实例方法。

The real implementation, straight out of NSObject.m is as such: 真正的实现,直接来自NSObject.m,是这样的:

- (BOOL)respondsToSelector:(SEL)aSelector {
        PF_HELLO("")
        return class_respondsToSelector( isa, aSelector );
}

Now, I have no idea why that PF_HELLO("") is there, but as you can see, it's literally ASKING the CLASS in the RUNTIME "Hey, do you have a method for this isa [instance] called aSelector?" 现在,我不知道为什么有PF_HELLO("") ,但是正如您所看到的,它实际上是在运行时PF_HELLO("") CLASS“嘿,您是否有一个用于此isa的方法,称为aSelector?”

And, in Objective-C, class methods ALSO belong to instances, but, however, take lower precedence (the instance method of the same name as the class method is called before the class method). 并且,在Objective-C中,类方法ALSO属于实例,但是优先级较低(与类方法同名的实例方法在类方法之前被调用)。

Another aspect of Objective-C's Dynamic Typing is that the id type is in fact declared as follows: Objective-C动态键入的另一个方面是, id类型实际上声明如下:

typedef struct objc_class *Class;
typedef struct objc_object {
    Class isa;
} *id;

So your instance object is in fact, a Class pointer. 因此,您的实例对象实际上是一个Class指针。 This means your -respondsToSelector messages go to the Class of the instance type as well. 这意味着您的-respondsToSelector消息也将转到实例类型的Class。 In your case, it means that -respondsToSelector is going to the objc_class FIRST. 在您的情况下,这意味着-respondsToSelector将转到objc_class FIRST。

Now in a test case, (straight out of libFoundation), my answer would be summed up like this: 现在在一个测试案例中(直接来自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