一.继承的概念
继承是面向对象的三大特征之一,也是实现软件复用的重要手段,Objective-C的继承具有单继承特点,每个子类只有一个父类。 继承可以使得子类具有父类的属性和方法或者重新定义、追加属性方法等。
例如:车和轿车、卡车的关系,轿车和卡车继承了车,它们是车的子类。
因为子类是一种特殊的父类,因此,父类包含的范围总比子类包含的范围要大,所以可以认为父类是大类,子类是小类。
二.继承的好处
- 提高了代码的复用性,能够大大缩短开发周期;
- 提高了代码的维护性;
- 让类和类之间产生了关系,是多态的前提;
三.一切从基类(NSObject)开始
继承就像一个倒过来的树一样,不断地分叉,这些树叉最初都是由数的根部分出来的,OC中的这个根就是NSObject类,所以我们以前定义类的形式为:
@interface 类名: NSObject
{
//成员变量声明
}
//方法声明
@end
即该类的父类为NSObject,该类就继承了NSObject。
而继承的子类继承父类的语法如下:
@interface 类名: 父类
{
//成员变量声明
}
//方法声明
@end
例如:
@interface SubClass: SuperClass
{
//成员变量声明
}
//方法声明
@end
从上面的语法格式看,定义子类的语法非常简单,只需在原来类的定义上增加“:SuperClass”即可,即表明该子类"SubClass"继承了"SuperClass"。
四.子类中只可以访问父类中interface中声明的变量和方法。
我们定义一个父类ClassA,它在interface中有变量x和方法initVar:
@interface ClassA: NSObject
{
int x;
}
- (void) initVar;
@end
@implementation
{
int y;
}
- (void) initVar {
x = 100;
y = 101;
}
@end
我们再定义一个子类ClassB,继承的ClassB中增加一个方法printVar来打印变量,printVar方法也就为子类扩展的新方法:
@interface ClassB: ClassA
- (void) printVar;
@end
@implementation
- (void) printVar {
NSLog(@"x = %i", x);
//NSLog(@"y = %i", y); //error,因为y是在父类的impletation中定义的,所以子类无法访问y。
}
@end
y会报错:
子类只能访问父类interface中声明的变量x,像implementation中的y变量在子类中是不能访问的。子类ClassB自动继承了ClassA中的initVar方法,是可以直接访问的,我们看main方法中的调用:
int main(int argc, char *argv[]) {
@autoreleasepool {
ClassB* obj = [[ClassB alloc] init];
[obj initVar];
[obj printVar];
}
return 0;
}
输出为:
五.程序如何找到正确的方法
这个很简单,当我们调用一个对象的方法时,先在这个对象的类和类别中找,如果找不到,就到父类和父类的类别中找,如果还找不到就到父类的父类和其类别中找,直到找到为止。注意,OC中为单继承,每个类只有一个父类,所以在这个唯一的父类中找即可。
六.重写父类的方法
继承常做的两件事:扩展方法和覆盖。继承不能减少或者删除父类的方法,但可以覆盖重写父类在interface中声明的方法。之前我们说过了OC如何查找正确方法,现在本身的类和类别中找,找到后就可以调用了,这就是覆盖父类的前提。
同样看之前写过的ClassA和ClassB:
@interface ClassA: NSObject
{
int x;
}
- (void) initVar;
@end
@implementation
- (void) initVar {
x = 100;
}
@end
我们在子类ClassB中重写initVar方法:
@interface ClassB: ClassA
- (void) printVar;
@end
@implementation
- (void) initVar {
x = 200;
}
- (void) printVar {
NSLog(@"x = %i", x);
}
@end
在main中调用一下看看x的值:
int main(int argc, char *argv[]) {
@autoreleasepool {
ClassB* obj = [[ClassB alloc] init];
[obj initVar];
[obj printVar];
}
return 0;
}
输出为:
此时ClassB实际上有一个变量x,两个方法initVar和printVar可用。
七.super关键字
如果需要在子类方法中调用父类被覆盖的实例方法,可使用super关键字来调用父类被覆盖的实例方法。
同样看之前写过的ClassA和ClassB:
@interface ClassA: NSObject
{
int x;
}
- (void) initVar;
@end
@implementation
- (void) initVar {
x = 100;
}
@end
我们在子类ClassB中重写initVar方法:
@interface ClassB: ClassA
- (void) ClassAinitVar;
- (void) printVar;
@end
@implementation
- (void) ClassAinitVar {
//在子类方法中通过super显式调用父类被覆盖的实例方法
[super initVar];
}
- (void) initVar {
x = 200;
}
- (void) printVar {
NSLog(@"x = %i", x);
}
@end
在main中调用一下看看x的值:
int main(int argc, char *argv[]) {
@autoreleasepool {
ClassB* obj = [[ClassB alloc] init];
[obj initVar];
[obj printVar];
[obj ClassAinitVar];
[obj printVar];
}
return 0;
}
输出为:
super是Objective-C提供的一个关键字,super用于限定该对象调用它从父类继承得到的属性和方法。