Objective-C 的方法原型及重载和覆盖
前一篇是:Objective-C 的方法原型及重载,本篇呢在上面的话题稍进一步延伸,方法原型中方法的覆盖关系就较为密切的,所以再说下方法覆盖的话题。
由前面,我们知道,在 Objective-C 中方法原型的得来是这样的,假如方法声明是:
-/+ (返回类型) 方法名: (参数1类型) 形参1 参数2描述: (参数2类型) 形参2 参数3描述: (参数3类型) 形参3 .......
那么它的方法原型则是:
方法名: 参数2描述:参数3描述: .......
关键的是它与参数类型无关。
重载的时候我们应避免方法原型相同,而子父类间进行方法覆盖的时候我们又应该要保持一样的方法原型。
我们可以再一次感性认识一下 Objective-C 的方法原型,从 Xcode 中,当 Command + 鼠标悬停 在调用方法上时:
在 Xcode 中把方法原型给标识了出来,把上蓝色部分连缀起来就是方法的原型,即签名,如上面两方法的签名分别是:
initWithFrame:
presentPopoverFromRect:inView:permittedArrowDirections:animated:
好了,该说今天的主要话题了,体现在多态中,要达到方法的动态绑定,必须使得父子类的两个方法具体一样的原型。因为传统的语方,如 Java/C++/C# 的方法原型与类型是悉悉相关的,对于相互覆盖的两个方法必须接受一样的类型; 而这对于 Objective-C 就宽松了许多。
比如在 Objective-C 中有这样的应用场景,当父类有个方法是, handleData: (Data1 *) data,子类有个方法是,handleData: (Data2 *) data; 虽然它们处理的数据类型不同,Data2 和 Data1 间也没有继承关系,但是它们的方法原型相同,所以可以形成覆盖,这时候当用父类型去引用子实例时,调用 handleData 时可以动态绑定到子类实现上去。
上面的说法会让人感觉太模糊,下面例子看下:
Data1 和 Data2 之间没有父子关系,也就是传入不同的参数类型时可以动态绑定到相不同的方法上去。更要命的是,当类型为 Data1 时执行的是 Parent 的 handleData: 方法,子类和父类的两个 handleData: 形成了互为重载的关系。
上面的代码会出现警告: warning: Semantic Issue: Incompatible pointer types sending 'Data2 *' to parameter of type 'Data1 *',所以一般来说我们会让 Data2 作为 Data1 的子类的。
Objective-C 的这种特性我们可以用在 IBAction 方法中,例如在 C# 的 WinForm 处理事件时是:
对于 sender 要转型为 Button,其实 IBAction 的默认形式大至如此:
但我们直接改变参数的类型,则为:
这样可以省去了强制转型,方法的表意也更直接。
到了这里,如果您还有点耐心的话,可以看看 Java 在碰到这种情况时的表现,运行如下代码:
对于 new Child() 其实是有两个 foo 方法的选择,可以调用 foo(Object),也可以调用 foo(String)。
<
永久链接 https://yanbin.blog/objectivec-prototype-overload-override/, 来自 隔叶黄莺 Yanbin's Blog
[版权声明]
本文采用 署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0) 进行许可。
由前面,我们知道,在 Objective-C 中方法原型的得来是这样的,假如方法声明是:
-/+ (返回类型) 方法名: (参数1类型) 形参1 参数2描述: (参数2类型) 形参2 参数3描述: (参数3类型) 形参3 .......
那么它的方法原型则是:
方法名: 参数2描述:参数3描述: .......
关键的是它与参数类型无关。
重载的时候我们应避免方法原型相同,而子父类间进行方法覆盖的时候我们又应该要保持一样的方法原型。
我们可以再一次感性认识一下 Objective-C 的方法原型,从 Xcode 中,当 Command + 鼠标悬停 在调用方法上时:
![]() | ![]() |
在 Xcode 中把方法原型给标识了出来,把上蓝色部分连缀起来就是方法的原型,即签名,如上面两方法的签名分别是:
initWithFrame:
presentPopoverFromRect:inView:permittedArrowDirections:animated:
好了,该说今天的主要话题了,体现在多态中,要达到方法的动态绑定,必须使得父子类的两个方法具体一样的原型。因为传统的语方,如 Java/C++/C# 的方法原型与类型是悉悉相关的,对于相互覆盖的两个方法必须接受一样的类型; 而这对于 Objective-C 就宽松了许多。
比如在 Objective-C 中有这样的应用场景,当父类有个方法是, handleData: (Data1 *) data,子类有个方法是,handleData: (Data2 *) data; 虽然它们处理的数据类型不同,Data2 和 Data1 间也没有继承关系,但是它们的方法原型相同,所以可以形成覆盖,这时候当用父类型去引用子实例时,调用 handleData 时可以动态绑定到子类实现上去。
上面的说法会让人感觉太模糊,下面例子看下:
1#import <Foundation/Foundation.h>
2
3@interface Data1 : NSObject
4-(void) foo;
5@end
6@implementation Data1
7-(void) foo {
8 NSLog(@"I'm Data1 type");
9}
10@end
11
12@interface Data2 : NSObject
13-(void) foo;
14@end
15@implementation Data2
16-(void) foo {
17 NSLog(@"I'm Data2 type");
18}
19@end
20
21@interface Parent : NSObject
22-(void) handleData: (Data1 *) data;
23@end
24@implementation Parent
25-(void) handleData:(Data1 *)data {
26 [data foo];
27}
28@end
29
30@interface Child : Parent
31-(void) handleData: (Data2 *) data;
32@end
33@implementation Child
34-(void) handleData:(Data2 *)data {
35 [data foo];
36}
37@end
38
39int main (int argc, const char * argv[])
40{
41
42 NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
43
44 Parent *pp = [[Child alloc] init];
45
46 Data2 *data2 = [[Data2 alloc] init];
47 [pp handleData:data2]; //这行输出 I'm Data2 type
48
49 Data1 *data1 = [[Data1 alloc] init];
50 [pp handleData:data1]; //这行输出 I'm Data1 type
51
52 [pool drain];
53 return 0;
54}Data1 和 Data2 之间没有父子关系,也就是传入不同的参数类型时可以动态绑定到相不同的方法上去。更要命的是,当类型为 Data1 时执行的是 Parent 的 handleData: 方法,子类和父类的两个 handleData: 形成了互为重载的关系。
上面的代码会出现警告: warning: Semantic Issue: Incompatible pointer types sending 'Data2 *' to parameter of type 'Data1 *',所以一般来说我们会让 Data2 作为 Data1 的子类的。
Objective-C 的这种特性我们可以用在 IBAction 方法中,例如在 C# 的 WinForm 处理事件时是:
1private void button_Click(object sender, EventArgs e)
2{
3 Button button = (Button) sender;
4 button.someAttribute = ......
5}对于 sender 要转型为 Button,其实 IBAction 的默认形式大至如此:
1- (IBAction) buttonClicked: (id) sender {
2 UIButton *button = (UIButton *) sender;
3 //do something with the button
4}但我们直接改变参数的类型,则为:
1- (IBAction) buttonClicked: (UIButton *) button {
2 //do something with the button
3}这样可以省去了强制转型,方法的表意也更直接。
到了这里,如果您还有点耐心的话,可以看看 Java 在碰到这种情况时的表现,运行如下代码:
1package cc.unmi;
2
3public class TestOverride {
4 public static void main(String[] args) {
5 Parent p = new Child();
6 p.foo("String"); //输出 Parent:foo,虽然 String 是 Object 的子类
7 p.foo(new Object()); //自然输出的是 Parent:foo
8
9 new Child().foo("String"); //只有这样才输出 Child:foo
10 }
11}
12
13class Parent {
14 public void foo(Object obj){
15 System.out.println("Parent:foo");
16 }
17}
18
19class Child extends Parent {
20 public void foo(String s){
21 System.out.println("Child:foo");
22 }
23}对于 new Child() 其实是有两个 foo 方法的选择,可以调用 foo(Object),也可以调用 foo(String)。
<
永久链接 https://yanbin.blog/objectivec-prototype-overload-override/, 来自 隔叶黄莺 Yanbin's Blog[版权声明]
本文采用 署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0) 进行许可。
