Objective-C中的多态性
问题:什么叫多态?
多态(Polymorphism),在面向对象语言中指的是同一个接口可以有多种不同的实现方式,OC中的多态则是不同对象对同一消息的不同响应方式,子类通过重写父类的方法来改变同一消息的实现,体现多态性。另外我们知道C++中的多态主要是通过virtual关键字(虚函数、抽象类等)来实现,具体来说指的是允许父类的指针指向子类对象,成为一个更泛化、容纳度更高的父类对象,这样父对象就可以根据实际是哪种子类对象来调用父类同一个接口的不同子类实现。
举个简单例子来展示OC的多态实现。假设有一个动物父类Animal,其下有两个子类,一个是Dog,一个是Cat,父类有一个统一接口:shout,表示动物的叫声,父类对接口有一个默认实现,子类各自有自己的接口实现,继承关系如下:
100.png
Animal父类:
// Animal.h @interface Animal : NSObject
/** * 父类接口,动物叫声 */
- (void)shout;
@end
// Animal.m #import "Animal.h"
@implementation Animal
/** * 父类接口的默认实现,无语 */
- (void)shout {
NSLog(@"... ...");
}
@end
Dog子类:
// Dog.h #import "Animal.h"
@interface Dog : Animal
/** * 重写父类接口,狗叫声 */
- (void)shout;
@end
// Dog.m #import "Dog.h"
@implementation Dog
/** * 重写父类接口,狗叫声 */
- (void)shout {
NSLog(@"汪汪汪,汪汪汪");
}
@end
Cat子类:
// Cat.h #import "Animal.h"
@interface Cat : Animal
/** * 重写父类接口,猫叫声 */
- (void)shout;
@end
// Cat.m #import "Cat.h"
@implementation Cat
/** * 重写父类接口,猫叫声 */
- (void)shout {
NSLog(@"喵喵喵,喵喵喵");
}
@end
多态性测试:
/* 1. 指向Animal父类对象的Animal父类指针 */
Animal *p_animal4animal = [[Animal alloc] init];
/* 2. 指向Dog子类对象的Animal父类指针 */
Animal *p_animal4dog = [[Dog alloc] init];
/* 3. 指向Cat子类对象的Animal父类指针 */
Animal *p_animal4cat = [[Cat alloc] init];
/* 向指向不同对象的父类指针发送相同的消息,期望得到各自不同的结果,实现多态 */
[p_animal4animal shout]; // 打印结果:... ... [p_animal4dog shout]; // 打印结果:汪汪汪,汪汪汪 [p_animal4cat shout]; // 打印结果:喵喵喵,喵喵喵
问题: Objective-C和Swift中有重载吗?
Swift中有重载,但Objective-C中基本不完全支持重载,事实上OC支持参数个数不同的函数重载。
重载、重写以及隐藏三者在编程语言中的定义
重载(overload):函数名相同,函数的参数列表不同(包括参数个数和参数类型),至于返回类型可同可不同。重载发生在同一个类的不同函数之间,是横向的。重载和多态性无关。
重写(override):指的是virtual函数的重写,用来体现多态性,指的是子类不想继承使用父类的方法,通过重写同一个函数的实现实现对父类中同一个函数的覆盖,因此又叫函数覆盖。重写的函数必须和父类一模一样,包括函数名、参数个数和类型以及返回值,只是重写了函数的实现。重写发生于父类和子类之间,是纵向的。
隐藏:OC中也没有隐藏,典型的C++中有,通过虚函数和父子类之间的函数重写进行区分,此处不再讨论。其中重载和重写是针对函数的,而隐藏除了函数还会针对成员变量。隐藏发生在父类和子类之间,隐藏指的是父类的同名函数或变量在子类中隐藏,其中只要函数同名就隐藏,不管参数相同与否。在子类中父类的同名函数或变量不可见,但在父类中依然存在。
Swift是基于C语言和OC语言优化后更加完善的新型语言,摆脱了C的兼容性限制,采用安全的编程模式并且增加了一些新的特性使编程更加有趣、友好,适应语言发展的趋势和期望。函数重载作为多态性的一个部分在Swift中是支持的,可能也是考虑到要弥补OC中不完全支持函数重载的这一缺陷。OC不完全支持重载,因为OC学习者应该会发现同一个类中不允许定义函数名相同且参数个数相同的两个函数,无论参数类型和返回值类型相同与否。但是说完全不支持也太绝对,因为OC中允许定义函数名相同但参数个数不同的两个函数,也就是说OC支持参数个数不同的函数重载。 例如,我们可以在一个类中定义两个参数个数不同的函数,调用时通过参数个数进行区分:
重载函数定义
- (void)test:(int)one;
- (void)test:(int)one andTwo:(int)two;
重载函数实现:
- (void)test:(int)one {
NSLog(@"one parameter!");
}
- (void)test:(int)one andTwo:(int)two {
NSLog(@"two parameters!");
}
多态调用:
[self test:1]; // output:one parameter! [self test:1 andTwo:2]; // output:two parameter!
可以看出OC可以通过参数个数实现函数重载,但是如果参数相同,无论参数和返回值类型相同与否都无法编译通过。下面的定义是无法通过xcode的编译的:
- (void)test:(int)one;
- (int)test:(float)one; // Duplicate declaration of method 'test'
问题:Object-C的类可以多重继承么?可以实现多个接口么?重写一个类的方式用继承好还是分类好?为什么?
Objective-C的类只支持单继承,不可以多重继承。可以利用protocol代理协议实现多个接口,通过实现多个接口完成类似C++的多重继承;在Objective-C中多态特性是通过protocol协议或者Category类别来实现的。protocol协议定义的接口函数可以被多个类实现,Category类别可以在不变动原类的情况下进行函数重写或者扩展。 一般情况用分类更好,因为用Category去重写类的方法,仅对本Category有效,不会影响到其他类与原有类的关系。
问题:Cocoa中有虚基类的概念么?怎么简洁的实现? Cocoa中没有虚基类的概念,虚基类是C++中为了解决多重继承二义性问题的,而OC中只有单继承,要实现类似C++中的多继承,可以通过protocal协议来简单实现,因为一个类可以实现多个协议,类似于Java中一个类可以实现多个接口。