1

在 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]. 它只是一个特殊的捷径,还是这背后有什么机制?

任何帮助将非常感激!提前致谢!

4

5 回答 5

4

来自Objective-C 编程语言对象、类和消息传递

所有对象、类和实例都需要到运行时系统的接口。类对象和实例都应该能够自省它们的能力并报告它们在继承层次结构中的位置。提供这个接口是 NSObject 类的职责。

因此 NSObject 方法不必实现两次——一次为实例提供运行时接口,再次为类对象复制该接口——<strong>类对象被赋予执行根类中定义的实例方法的特殊权限。当一个类对象收到一个它不能用类方法响应的消息时,运行时系统会判断是否有根实例方法可以响应。类对象可以执行的唯一实例方法是在根类中定义的实例方法,并且只有在没有可以完成这项工作的类方法的情况下

在这种情况下,NSObject是根类。由于NSObject实例都符合NSObject protocol, where-respondsToSelector:定义的,大多数类对象应该能够执行-respondsToSelector:.

于 2012-03-12T07:05:12.920 回答
4

Q1:

简单的答案是,除了类方法之外,您还可以在类对象上调用根类的任何实例方法(无论您的类的根类是什么;在本例中为NSObject)。

更复杂的答案是类对象是元类的实例。而实例方法是实例上的方法,在类中定义;类方法是类对象上的方法,在元类中定义。每个类都有自己的元类。元类的继承遵循其类的继承;ieNSString的元类继承自NSObject的元类。最终,根类的元类继承自根类;ieNSObject的元类继承自NSObject. 这就是为什么 NSObject 的所有实例方法都可用于类对象的原因。

Q2:

[Square class]调用类方法+class(这与 无关-class)。+class本质上是一个身份方法,它简单地返回调用它的东西(就像-self);即 iffoo是指向类对象的指针,则[foo class]foo.

所以+class看起来很没用;我们为什么用它?这是因为在 Objective-C 语言的语法中,类名不是有效的表达式(与 Smalltalk 不同)。所以你不能说id bar = Square;; 那不会编译。作为语法中的一个特例,允许在消息调用表达式中使用类名代替接收者,并将消息发送到类对象;即[Square something]。因此,如果您想在任何其他表达式上下文中使用类对象,我们可以通过调用类似的标识方法以迂回的方式做到这一点+class;ie[Square class]是一个可以在任何表达式上下文中使用的表达式([Square self]也可以,但我们[Square class]按照惯例使用,这是不幸的,因为它与 混淆了-class);我们本来希望只使用Square,但不能由于语言。

在您的情况下,它已经是消息调用表达式中的接收者,因此无需执行[Square class]; Square已经在那里工作了。

于 2012-05-31T20:39:30.553 回答
0

//第一季度:

既然-respondsToSelector:是实例方法,不是类方法,那为什么可以直接在Square类上使用呢?//

您似乎认为不能从实例方法调用类方法(反之亦然)。相反,这似乎是方法 -respondsToSelector 这样做的意图,很可能是通过使用 -class 方法获取发送者的类,然后查询该类是否响应选择器并返回 YES 或 NO。在更本地化的示例中,请考虑以下内容:

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

在 Objective-C 中完全有效,前提是 MyCurrentClass 已全部分配和初始化。

//Q2:

这本书说你可以在这里使用 Square 而不是 [Square class]。只是一个特殊的捷径,还是这背后有什么机制?//

将 -class 发送到 Class 是完全多余的!这没什么意义,只是额外的不必要的代码。-class 只查询接收者的类,不管它是实例还是类对象。

于 2012-03-12T05:43:22.393 回答
0

Objective C 运行时当前将一个类实现为某个其他类的实例对象。因此,一个类将响应某些实例方法。

于 2012-03-12T09:17:57.573 回答
-1

直接来自 NSObject.m 的真正实现是这样的:

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

现在,我不知道为什么PF_HELLO("")会这样,但正如你所看到的,它实际上是在 RUNTIME 中询问类“嘿,你有这个 isa [instance] 的方法,称为 aSelector 吗?”

而且,在Objective-C中,类方法也属于实例,但是优先级较低(与类方法同名的实例方法在类方法之前被调用)。

Objective-C 的动态类型的另一个方面是id类型实际上声明如下:

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

所以你的实例对象实际上是一个类指针。这意味着您的 -respondsToSelector 消息也会转到实例类型的类。在您的情况下,这意味着 -respondsToSelector 将首先进入 objc_class。

现在在一个测试用例中,(直接从 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];
于 2012-03-12T06:27:11.160 回答