继承(五)
继承
继承,是面向对象三大特征之一,继承的出现,是为了减少很多的冗余代码,因为是把各个类中,把相同特征和行为收集到另一个类中,然后这些类继承于这个集中类,可以把这个集中类的所有的特征和行为都继承过来。然后使用。
说到类了,则应该提到子类和父类,这两者是同时存在的,不能说我是父类,也不能说我是子类,两者相互依存。谁是谁的父类,谁是谁的子类。
子类继承父类,是单向的,两者之间不能相互继承。
继承还具有传递性,用白话来说就是:B类如果继承于A类,C类继承于B类,此时B类就是A类的子类,C类就是B类的子类;A是B的父类,B是C的父类。这样的话,B(子类)就可以继承了A(父类)的所有实例变量和方法,这里说得是所有的实例变量和方法,也包括private修饰的实例变量,但是private修饰的实例变量,继承过来后不能直接使用。C(子类)继承了B(父类)的所有实例变量和方法,包括private修饰的实例变量。
在OC中,所有的类的最终父类都是NSObject(这个类没有父类了,叫做根类)。
子类继承过来的父类方法,可以重写。这里注意的是,1、重写父类方法,不需要再声明,就是不需要在.h文件里声明。2、重写父类方法,必须与父类方法名一样。
一个练习:
Animal.h:继承于NSObject
#import <Foundation/Foundation.h> @interface Animal : NSObject{ NSString *_kind; NSString *_color; @private NSString *_name; } - (void)sayHi;
- (NSString *)kind; - (NSString *)color;
- (void)setKind :(NSString *)kind; - (void)setColor :(NSString *)color; @end |
Animal.m
#import "Animal.h" @implementation Animal - (void)sayHi{ NSLog(@"动物乱叫..."); } - (NSString *)kind{ return _kind; } - (NSString *)color{ return _color; } - (void)setKind :(NSString *)kind{ _kind = kind; } - (void)setColor :(NSString *)color{ _color = color; } @end |
Cat.h:继承于Animal类
//继承的时候,使用#import引入父类头文件 #import "Animal.h" @interface Cat : Animal{ - (void)test; @end |
Cat.m
#import "Cat.h" @implementation Cat - (void)test{ _kind = @" "; _color = @" "; // _name = @" "; } //重写父类继承过来的方法。 //1、不用声明 //2、方法名必须与父类的相同 - (void)sayHi{ NSLog(@"喵了个咪的.."); }
@end |
SmallCat.h:继承于Cat类
#import "Cat.h" @interface SmallCat : Cat @end |
SmallCat.m
#import "SmallCat.h" @implementation SmallCat @end
|
main.m
#import <Foundation/Foundation.h> #import "Cat.h" #import "SmallCat.h" int main(int argc, const char * argv[]) { @autoreleasepool {
Cat *c = [[Cat alloc]init]; [c sayHi];//2015-04-15 10:47:46.528 OCLesson3_继承[951:41625] 动物乱叫... [c setKind:@"哈士奇.."]; [c setColor:@"白色.."]; NSLog(@"%@",c.kind);//2015-04-15 10:54:16.185 OCLesson3_继承[1045:44417] 哈士奇.. NSLog(@"%@",c.color);//2015-04-15 10:54:16.186 OCLesson3_继承[1045:44417] 白色..
c.kind = @"蓝猫.."; c.color = @"白色的...";
NSLog(@"%@",c.kind);//2015-04-15 10:54:16.186 OCLesson3_继承[1045:44417] 蓝猫.. NSLog(@"%@",c.color);//2015-04-15 10:54:16.186 OCLesson3_继承[1045:44417] 白色的... [c sayHi];
SmallCat *sc = [[SmallCat alloc] init]; [sc sayHi];
} return 0; } |
self:
self是一个指向自己的指针(可以打印出来看地址),一般用在类内,用来调用自己的方法,当然这里得说明白,self如果在实例方法里,只能调实例方法,在类方法里只能调类方法,不允许在实例方法里调类方法,在类方法里调实例方法。
self调自身的方法形式为:[self 本类方法名]。
super:
super 的出现,是因为在子类里,self调了自己重写父类的方法时,执行的是本类中的重写后的方法(若是本类中没有调用的方法,系统会自动去父类找,若是父类没有这个方法,就会去父类的父类找,一直找到NSObject根类,若是还没有,则程序崩溃),此时若是想执行父类的那个方法,那么就用到了super来调用父类的方法。
self是指针,super不是指针,也不是变量,super是编译器的指令。专门用来访问父类的方法。若是在本类中使用super(将其当成变量赋值或者当成指针打印地址等),都会抱一个错误,叫做“未定义super”。
super与self一样,在实例变量里只能调用实例方法,在类方法里只能调用类方法,不允许在实例方法里调类方法,在类方法里调实例方法。
一个例子:
Person.h:继承NSObject
#import <Foundation/Foundation.h> @interface Person : NSObject - (void) sayHi; - (void)test; + (void)test2; + (void)test3; @end |
Person.m
#import "Person.h" @implementation Person - (void) sayHi{ NSLog(@"吃了吗?"); } //测试self - (void)test{ //self就是一个指向自己的指针。 //self作用:在类内调用自己的方法。 //self在实例方法中可以调用实例方法, NSLog(@"%p",self);//2015-04-15 14:50:24.972 OCLesson3_Self_Super[1733:91671] 0x100114040 [self sayHi]; //self在实例方法中,不能调用类方法。 // [self test2]; }
+ (void)test2{ //self在类方法中可以调用类方法。 [self test3]; //self在类方法中,不能调用实例方法。 // [self test]; } + (void)test3{ } @end |
Student.h:继承于Person
#import "Person.h" @interface Student : Person - (void)stuTest; + (void)stuTest1; @end |
Student.m
#import "Student.h" @implementation Student - (void)sayHi{ NSLog(@"约吗?"); }
//测试super - (void)stuTest{ //super可以调用父类的方法。 //super是一条编译器指令(不是变量,也不是指针),用来访问父类方法。 // NSLog(@"%p",super);//报错super未定义 [super sayHi]; [super test];//实例方法里可以调实例方法。 // [super test2];//实例方法不能调类方法。 }
+ (void)stuTest1{ [super test2]; [super test3];//super在类方法中调用类方法 // [super test];//super在类方法不能调用实例方法。 } @end |
main.m
#import <Foundation/Foundation.h> #import "Person.h" #import "Student.h" int main(int argc, const char * argv[]) { @autoreleasepool { // Person *p1 = [[Person alloc] init]; // NSLog(@"%p",p1);//2015-04-15 14:50:24.972 OCLesson3_Self_Super[1733:91671] 0x100114040 //// [p1 sayHi]; // [p1 test];//2015-04-15 14:49:07.067 OCLesson3_Self_Super[1710:90956] 吃了吗?
Student *stu = [[Student alloc] init]; [stu sayHi];//2015-04-15 15:04:35.318 OCLesson3_Self_Super[1798:96571] 约吗? [stu stuTest];//2015-04-15 15:05:43.319 OCLesson3_Self_Super[1811:97083] 吃了吗? } return 0; } |
完整的初始化方法:
完整的初始化方法,要加上一个判断,就是遵循一个顺序,先对父类进行初始化,如果父类初始化成功后,就给子类返回一个地址,子类接收到地址后,再给自己初始化。如果在父类初始化的时候,失败了,就会给子类返回一个空值,子类接收到这个空值后,不再执行自己的初始化方法,而是把空值返回出去,让程序停止下来。
上面实现过程代码如下:
Person.h继承于NSObject
#import <Foundation/Foundation.h>
@interface Person : NSObject{ NSString *_name; NSInteger _age; } - (instancetype)initWithName :(NSString *)name age:(NSInteger)age; @end |
Person.m
#import "Person.h" @implementation Person - (instancetype)initWithName :(NSString *)name age:(NSInteger)age{ //初始化从父类继承过来的东西。 //1.初始化失败,返回值为nil(空) //2.初始化后会换地址,(NSMutableArray)类簇 self = [super init]; //一旦从父类继承的东西初始化失败,就不要再给自己的实例变量赋值了。 if(self){ _name = name; _age = age; } //如果初始化失败,返回nil //如果初始化成功返回地址。 return self; } @end //指定初始化方法(课后找找) |
这里,先用super 调用父类(NSObject)的init初始化方法,然后用self指针来接收[super init]返回的地址。如果父类的初始化失败,返回一个空给self,当判断self的值,如果不为空,才为自己的类进行初始化,否则返回空出去。
值得注意的是:[super init]指的是super调用的是父类的的初始化方法,不一定是init,也可能initXXX。(一般是继承之后再有一个子类继承它,才会出现这种状况)。
一个例子:
Student.h继承于Person
自己声明了一个初始化方法,在实现初始化方法时,先用self接收[super initXXX ]父类初始化的结果,如果成功,返回地址后自己继续进行初始化。
#import "Person.h" @interface Student : Person{ NSString *_num; } - (instancetype)initWithName:(NSString *)name age:(NSInteger)age num:(NSString *)num; @end |
Student.m
#import "Student.h" @implementation Student - (instancetype)initName:(NSString *)name age:(NSInteger)age num:(NSString *)num{ self = [super initName:name age:age]; if (self) { _num = num; } return self; } @end |
self = [super initName:name age:age];这一句就是说,super调了父类(Person)的初始化方法,如果成功,self接收地址,后继续执行_num = num;这个自己的初始化。
相同的:
CollageStudent.h继承于Student
#import "Student.h" @interface CollageStudent : Student{ NSString *_major; } - (instancetype)initName:(NSString *)name age:(NSInteger)age num:(NSString *)num major: (NSString *)major;
@end |
CollageStudent.m
#import "CollageStudent.h" @implementation CollageStudent - (instancetype)initName:(NSString *)name age:(NSInteger)age num:(NSString *)num major: (NSString *)major{ self = [super initName:name age:age num:num]; if (self) { _major = major; } return self; } @end |
都是一样的执行方式,一步一步往上,从最高的父类开始初始化,子类self接收,判断后才对自己初始化。
便利构造器:
便利构造器是一个类方法,目的是为了方便使用者的使用,但是在写便利构造器的时候,会稍微麻烦。
注意的是:
1、便利构造器的出现,必须与对应的初始化方法一起出现;
2、方法名是以类名开头(类名必须全部小写),后面跟着初始化方法init后面的全部东西。如:初始化方法是:
- (instancetype ) initName :(NSString *)name age :(NSInteger)age;
则便利构造器为:(Person类)
+ (instancetype)personName :(NSString *)name age :(NSInteger)age;
便利构造器的实现,是把alloc这个内存分配的步骤放在构造器里,在main.m等里使用便利构造器的时候,不需要再alloc。
例子:
Person.h
#import <Foundation/Foundation.h> @interface Person : NSObject{ NSString *_name; NSInteger _age; } - (instancetype ) initName :(NSString *)name age :(NSInteger)age;
//便利构造器(类方法) //写便利构造器前提要有一个对应的初始化方法 //方法名以类名开头,类名小写,后面跟着init后面的所有东西。 + (instancetype)personName :(NSString *)name age :(NSInteger)age;
@end |
Person.m
#import "Person.h" @implementation Person - (instancetype ) initName :(NSString *)name age :(NSInteger)age{ self = [super init]; if (self) { _name = name; _age = age; } return self; } //便利构造器实现 + (instancetype)personName :(NSString *)name age :(NSInteger)age{
Person *p = [[Person alloc]initWithName:name age:age]; return p; } @end |
Person *p = [[Person alloc]initName:name age:age];这句的意思是,把本该在main里alloc的步骤,移到.m实现文件中。
main.m
#import <Foundation/Foundation.h> #import "Person.h" int main(int argc, const char * argv[]) { @autoreleasepool { Person *p = [[Person alloc]initName:@"小黑" age:18]; Person *p2 = [Person personName:@"小白" age:18]; } return 0; } |
Person *p = [[Person alloc]initName:@"小黑" age:18]; 这个语句是直接调用初始化方法,需要alloc;
Person *p2 = [Person personName:@"小白" age:18];这个语句是调用便利构造器,不需要alloc。相对于alloc,方便很多。