什么是多态?
面向对象语言的三大特性,继承,封装和多态。其中封装和继承很容易理解,那么继承和多态就是相辅相成的两个特性。什么是多态?就是面向对象语言中同一个接口可以有不同的实现方式,OC中的多态是不同对象对同一消息的不同响应方式,子类通过重写父类的方法来改变同一方法的实现,体现多态性。另外我们知道C++中的多态主要是通过virtual关键字(虚函数、抽象类等)来实现,具体来说指的是允许父类的指针指向子类对象,成为一个更泛化、容纳度更高的父类对象,这样父对象就可以根据实际是哪种子类对象来调用父类同一个接口的不同子类实现。
举个简单的例子,有父类People以及两个子类Man和Women,父类有个默认的方法workHard,两个子类有各自的重写实现。
父类
@interface People : NSObject
- (void)workHard;
@end
@implementation People
- (void)workHard{
NSLog(@"people work 。。。。。。。");
}
@end
子类
@implementation Man
// 重写父类
- (void)workHard{
NSLog(@"Man make money");
}
@end
@implementation Women
// 重写父类
- (void)workHard{
NSLog(@"Women look after children");
}
@end
多态测试结果:
// 指向People父类的People父类指针
People *p1 = [[People alloc] init];
// 指向Man子类的People父类指针
People *p2 = [[Man alloc] init];
// 指向Women子类的People父类指针
People *p3 = [[Women alloc] init];
[p1 workHard]; // people work 。。。。。。。
[p2 workHard]; // Man make money
[p3 workHard]; // Women look after children
实际项目中可以让控制器为例做这系列继承关系,通过baseViewController的基本逻辑,继承出不同子类对同一方法的不同实现。
Q1:Objective-C和Swift中有重载吗?
Swift中有重载,但Objective-C中基本不支持重载。
首先,Objective-C中不完全支持重载,网上很多人要么将重载和重写搞混,要么说OC不支持重载(当然按照重载严格定义说OC不支持重载也没错),事实上OC支持参数个数不同的函数重载。
重载:函数名相同,函数的参数列表不同(包括参数个数和参数类型),至于返回类型可同可不同。重载发生在同一个类的不同函数之间,是横向的。重载和多态性无关。
- 函数名相同,参数名/参数类型/参数个数不同
- 重载函数并不仅仅局限于构造函数
- 函数重载是面相对象程序设计语言的重要标志
- OC 不支持函数重载,OC 的替代方式是 withXXX… 因此有些地方说OC不完全支持重载,由于参数名不同会导致方法名自然变化,也可以说OC不支持重载
重写:指的是virtual函数的重写,用来体现多态性,指的是子类不想继承使用父类的方法,通过重写同一个函数的实现实现对父类中同一个函数的覆盖,因此又叫函数覆盖。重写的函数必须和父类一模一样,包括函数名、参数个数和类型以及返回值,只是重写了函数的实现。重写发生于父类和子类之间,是纵向的。
- 也叫覆盖,指在子类中定义一个与父类中方法同名同参数列表的方法。
- 重写是子类的方法覆盖父类的方法,要求方法名和参数都相同
- 因为子类会继承父类的方法,而重写就是将从父类继承过来的方法重新定义一次,重新填写方法中的代码。
- 重写必须继承(纵向),重载不用(横向)
Swift是基于C语言和OC语言优化后更加完善的新型语言,摆脱了C的兼容性限制,采用安全的编程模式并且增加了一些新的特性使编程更加有趣、友好,适应语言发展的趋势和期望。函数重载作为多态性的一个部分在Swift中是支持的,可能也是考虑到要弥补OC中不完全支持函数重载的这一缺陷。OC不完全支持重载,因为OC学习者应该会发现同一个类中不允许定义函数名相同且参数个数相同的两个函数,无论参数类型和返回值类型相同与否。但是说完全不支持也太绝对,因为OC中允许定义函数名相同但参数个数不同的两个函数,也就是说OC支持参数个数不同的函数重载。
- (void)test:(int)one;
- (void)test:(int)one two:(int)two;
- (void)test:(int)one {
NSLog(@"one parameter!");
}
- (void)test:(int)one two:(int)two {
NSLog(@"two parameters!");
}
[self test:1]; // output:one parameter!
[self test:1 two:2]; // output:two parameter!
虽然可以简单的认为是多个参数的重载,但是严格意义上来讲方法名是根据参数变化了,所以可以说不完全支持或者就说不支持重载。
- (void)test:(int)one;
- (int)test:(float)one; // Duplicate declaration of method 'test'
如果方法名参数一样,这种是编译不过的
Swift 例子
class Person: NSObject {
var name: String // 姓名
var age: Int // 年龄
// 构造函数
init(name: String,age: Int) {
self.name = name
self.age = age
// 必须在 super.init() 之前 初始化对象
super.init()
}
}
继承重写
class Student: Person {
var lesson: String
/// 重写
///
/// - Parameters:
/// - name: 姓名
/// - age: 年龄
override init(name: String, age: Int) {
lesson = "Python" // 必须放在super.init()之前
super.init(name: name, age: age)
}
}
/// 重写实例化的对象
let s = Student(name: "Joyce", age: 18)
print(s.lesson) // Python
通过重载的方法为其添加属性
参数名一样,参数增加了
class Student: Person {
var lesson: String
/// 重载
///
/// - Parameters:
/// - name: 姓名
/// - age: 年龄
/// - lesson: 课程
init(name: String, age: Int, lesson:String) {
self.lesson = lesson
super.init(name: name, age: age)
}
}
/// 重载实例化的对象
let s1 = Student(name: "帅哥", age: 21, lesson: "HTML 5")
print(s1.lesson) // HTML 5
总结:
- 通过重载,可以快速为方法添加新的属性,属性可以通过外部传入(很多库的实现都会重载很多方法)
- 重写,只能在方法内部设置属性,外部无法直观看到类的参数列表 (纵向继承实现多态)
Q2.Object-C的类可以多重继承么?可以实现多个接口么?重写一个类的方式用继承好还是分类好?为什么?
Objective-C的类只支持单继承,不可以多重继承。可以利用protocol代理协议实现多个接口,通过实现多个接口完成类似C++的多重继承;在Objective-C中多态特性是通过protocol协议或者Category类别来实现的。protocol协议定义的接口函数可以被多个类实现,Category类别可以在不变动原类的情况下进行函数重写或者扩展。
一般情况用分类更好,因为用Category去重写类的方法,仅对本Category有效,不会影响到其他类与原有类的关系
Q3.Cocoa中有虚基类的概念么?怎么简洁的实现?
Cocoa中没有虚基类的概念,虚基类是C++中为了解决多重继承二义性问题的,而OC中只有单继承,要实现类似C++中的多继承,可以通过protocal协议来简单实现,因为一个类可以实现多个协议,类似于Java中一个类可以实现多个接口。