在目标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]
。它只是一个特殊的捷径,还是背后有任何机制?
任何帮助都会非常感激!提前谢谢!
答案 0 :(得分:4)
来自 Objective-C编程语言,Objects, class, and Messaging,
所有对象,类和实例都需要一个接口 运行时系统。类对象和实例都应该能够 对自己的能力进行反思并报告自己的能力 继承层次结构。这是NSObject类的省份 提供这个界面。
因此NSObject方法不必实现两次 - 一次 为实例提供运行时接口,并再次复制它 类对象的接口 - 类对象被赋予特殊的分配来执行根类中定义的实例方法。 当一个类对象收到一条消息时,它无法用a响应 类方法,运行时系统确定是否有根 可以响应的实例方法。 仅实例方法a class对象可以执行的是那些在根类中定义,而只有在没有类方法可以完成工作时才会执行。
在这种情况下,NSObject
是根类。由于NSObject
个实例都符合NSObject protocol
,其中定义了-respondsToSelector:
,因此大多数类对象应该能够执行-respondsToSelector:
。
答案 1 :(得分:4)
Q1:
简单的答案是,除了类方法之外,您还可以在类对象上调用根类的任何实例方法(无论类的根类是什么,在本例中为NSObject
)。
更复杂的答案是类对象是元类的实例。实例方法是实例上的方法,它们在类中定义; class方法是类对象的方法,它们在元类中定义。每个类都有自己的元类。元类的继承遵循其类的继承;即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
之类的标识方法以环形方式执行此操作; ie [Square class]
是一个可以在任何表达式上下文中使用的表达式([Square self]
也可以使用,但我们按惯例使用[Square class]
,这是不幸的,因为它与{{1 }});我们本来希望只使用-class
,但不能使用该语言。
在您的情况下,它已经是消息调用表达式中的接收者,因此不必执行Square
; [Square class]
已在那里工作。
答案 2 :(得分:0)
// Q1:
因为-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只查询接收者的类,无论它是实例还是Class对象。
答案 3 :(得分:0)
Objective C运行时当前将一个类实现为某个其他类的实例对象。因此,一个类将响应某些实例方法。
答案 4 :(得分:-1)
真正的实现,直接来自NSObject.m就是这样:
- (BOOL)respondsToSelector:(SEL)aSelector {
PF_HELLO("")
return class_respondsToSelector( isa, aSelector );
}
现在,我不知道为什么PF_HELLO("")
存在,但正如你所看到的,它确实在RUNTIME中提出了CLASS“嘿,你有一个方法,这个isa [实例]叫做aSelector吗? “
而且,在Objective-C中,类方法也属于实例,但是,优先级较低(在类方法之前调用与类方法同名的实例方法)。
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];