发送消息给self和super
@注意:OC中的self和JAVA中的self有些不同:
1> 在动态方法中,self代表着"对象"
2> 在静态方法中,self代表着"类"
3> 万变不离其宗,记住一句话就行了:self代表着当前方法的调用者
Objective C提供了两个保留字self和super,用于在方法定义中引用执行该方法的对象。例如,假设你定义了一个方法reposition,用来修改它所处理的某个对象的坐标,这时可以调用setOrigin::方法来完成修改。要实现这个目标,要做的工作就是将setOrigin::消息发送给一个对象,这个对象与reposition消息的接收对象是同一个。在编写代码时,你既可以使用self,也可以使用super来引用该对象,reposition方法可以是:
[cpp]view plaincopy
-reposition
{
...
[self setOrigin:someX :someY];
...
}
也可以是:
[cpp]view plaincopy
-reposition
{
...
[super setOrigin:someX :someY];
...
}
这里,selfe和super都引用了接收reposition消息的对象,也可以是任何其它正好被使用的对象。但是,这两个保留字是很不相同的。self是消息机制传递给每个方法的一个隐含参数,在方法的实现中可以自由使用它的本地变量,正如实例变量的名称一样。super仅在作为消息表达式中的接收者时代替self。作为接收者,这两个保留字在如何影响消息进程上有着根本不同:
1:self是类的隐藏的参数,指向当前调用方法的类,另一个隐藏参数是_cmd,代表当前类方法的selector。
2:super并不是隐藏的参数,它只是一个"编译器指示符",它和self指向的是相同的消息接受者
self以常用的方式搜索方法的实现,起始于接收对象类的dispatch表。在上述例子中,它从接收repositon消息的对象类开始。
super是一个标记,告诉编译器在一个不同的地方搜索方法的实现。它起始于定义了super所在方法的类的超类。在上述例子中,它将从定义了repositon的类的超类开始。
只要super收到消息,编译器将为objc_msgSend替代另外一种消息机制,该消息机制直接查看定义了该类的超类,也就是发送消息给super的类的超类,而不是接收消息的对象的类。
例子:使用self和super
在使用有层级关系的三个类时,self和super之间的不同之处就很清楚了。例如,假设创建一个名为Low类的对象,Low类的超类是Mid,而Mid类的超类是High。这三个类都定义了名为negotiate的方法,分别被各个类用于自己的目的。另外,Mid定义了一个任意的方法称为makeLastingPeace,它调用了negotiate方法。
假设makeLastingPeace的实现(在Mid类中)使用了self指明了向其发送negotiate消息的对象:
[cpp]view plaincopy
- makeLastingPeace
{
[self negotiate];
... }
当向Low对象发送一条消息执行makeLastPeace方法,makeLastingPeace发送一条negotiate消息给相同的Low对象。这种消息机制将找到Low,也就是self所在的类中定义的negotiate。
但是,如果makeLastingPeace的实现使用super作为接收者:
[cpp]view plaincopy
[cpp]view plaincopy
- makeLastingPeace
{
[super negotiate];
... }
消息机制将找到定义在High中的negotiate。它会忽略接收makeLastingPeace消息的对象的类(Low),并跳到Mid的超类,因为Mid是定义makeLastingPeace的地方,所以不会是Mid中的negotiate。
正如该例所示,super提供了一种绕过重写了其他方法的方法的一种途径。在这里,super的使用使makeLastingPeace绕过了Mid中negotiate方法,这个方法重写了High中的同名方法。
同时,它也无法获得Mid中的negotiate。这看上去似乎使一个缺陷,但在下述环境中它是有意这么做的:
Low类的作者有意重写了Mid的negotiate,因此Low的实例(和它的超类)将会调用重定义后的方法。但Low的设计者不希望Low对象执行被继承的方法。
Mid方法makeLastingPeace的作者,在发送negotiate消息给super(如第二个实现代码所示)时,有意跳过Mid的negotiate(以及任何可能在Low类中定义并继承自Mid的方法)去执行定义在High类中的版本。但makeLastingPeace的第二个实现版本的设计者希望使用High中的negotiate而非其他。
Mid中的negotiate仍然可以被使用,但它需要向Mid实例发送直接的消息。
使用super
向super发送消息可以将方法实现分发给一个以上的类。可以重写一个已有的方法来修改或将它添加到现有的方法中,仍然可以将原始的方法纳入到修改后的方法当中。
[cpp]view plaincopy
- negotiate {
...
return[super negotiate];
}
对于某些任务,每个继承层次结构中的类都能实现一个方法处理部分工作,然后向super传递消息让其完成剩余工作。init方法初始化新分配的实例,它就是被设计用来达成上述目的的。每个init方法负责初始化类中定义的实例变量。但在这么做之前,它会发送一条init消息给super,通知被它的超类初始化它们的实例变量。每种版本的init都遵循这个过程,因此类会按照继承的顺序来初始化实例变量。
[cpp]view plaincopy
- (id)init {
self = [super init];
if(self) {
... }
}
如果想关注定义在超类中一个方法的核心功能,可以在子类中通过向super发送消息将该方法合并进来。例如,创建实例的每个类方法必须为新的对象分配存储空间,并初始化它的isa变量。分配空间的任务交给定义在NSObject类中的alloc和allocWithZone:方法去完成。如果别的类重写了这些方法(极少见的情况),它仍然可以通过发送一条消息给super来获得基本功能。
重定义self
super仅是一个面向编译器的标记,告诉编译器从哪里开始搜索方法来执行,它仅被用于消息的接收者。但self是一个变量名,用途比较多,设置可以被赋予一个新的值。
有一种趋势:在类方法的定义中实现。类方法通常不关心类对象,而是关心类的实例。例如,很多类方法都实现了实例的空间分配和初始化,并且通常会在同时建立起实例变量。在这样的方法中,向新分配的实例发送消息以及调用实例的self,这么做看上去很有吸引力,就像在一个实例方法中一样。但是这么做会报错。self和super都指向消息的接收对象,也就是接收该消息并被通知执行方法的对象。在实例方法中,self指向实例,但在类方法中,self指向的类对象。下面是个错误的例子:
[cpp]view plaincopy
+ (Rectangle *)rectangleOfColor:(NSColor *) color
{
self = [[Rectangle alloc] init];// BAD
[self setColor:color];
returnself;
}
为了避免这种情况,一种更好的方式是使用变量,而不是self来引用类方法种的实例:
[cpp]view plaincopy
+ (id)rectangleOfColor:(NSColor *)color
{
id newInstance = [[Rectangle alloc] init];// GOOD
[newInstance setColor:color];
returnnewInstance;
}
实际上,比起发送alloc消息给类方法中的类这种方式来说,更好的一种方法是发送alloc给self。这种方式,如果类是子类,并且rectangleOfColor:消息是由一个子类接收的,那么返回的实例与子类是相同类型(例如,NSArray的方法array被NSMutableArray继承)。
@不过,我觉得这里最好写成---- id newInstance = [[[self class] alloc ] init]
[cpp]view plaincopy
+ (id)rectangleOfColor:(NSColor *)color
{
id newInstance = [[self alloc] init];// EXCELLENT
[newInstance setColor:color];
returnnewInstance;
}