文章目录
- 一、继承
- 1.继承的定义
- 2.继承的特点
- 3.对父类方法的重写
- 4.super关键字
- 二、多态
- 1.什么时候会出现多态?
- 三、指针变量的强制类型转换
- 1.指针变量的强制类型转换
- 2.判断指针变量的实际类型
一、继承
1.继承的定义
继承是面向对象的三大特征之一,OC的继承具有单继承的特点,即每个子类只能有一个父类。
继承的本质是一种“由一般到特殊的关系”,子类是一种特殊的父类。
也就是说,子类拓展了父类。父类派生了子类。每个类只能有一个直接父类,但是可以有很多间接父类。NSbject是多有类的父类。
2.继承的特点
当子类继承父类时,将继承得到父类的如下东西:
1,全部成员变量
2,全部方法
#import <Foundation/Foundation.h>
//类的接口部分
@interface Fruit : NSObject
@property(nonatomic, assign)double weight;
- (void)info;
@end
//类的实现部分
@implementation Fruit
@synthesize weight;
- (void)info {
NSLog(@"我是一个水果,重%gg", weight);
}
@end
//子类的接口
@interface Apple : Fruit
@end
//子类的实现
@implementation Apple
@end
//主函数
int main() {
@autoreleasepool {
Apple *a = [[Apple alloc] init];
a.weight = 55;
[a info];
}
}
这里是代码运行结果:
可以看到子类Apple继承了父类的weight
3.对父类方法的重写
子类是特殊的父类,要体现出子类的特殊性时就需要对父类方法进行重写。
#import <Foundation/Foundation.h>
//类的接口
@interface Bird : NSObject
- (void)fly;
@end
//类的实现
@implementation Bird
- (void)fly {
NSLog(@"我是鸟,我会飞");
}
@end
//子类Ostrich的接口
@interface Ostrich : Bird
- (void)fly;
@end
//子类的实现
@implementation Ostrich
- (void)fly {
//对父类Bird的重写
NSLog(@"我是鸵鸟,我不会飞");
}
@end
int main() {
@autoreleasepool {
Bird *a = [[Bird alloc] init];
//父类的输出
[a fly];
Ostrich *b = [[Ostrich alloc] init];
//子类的输出
[b fly];
}
}
4.super关键字
当我们重写父类方法后,原本父类中的方法会被覆盖,此时如果想在子类中调用父类被覆盖的方法,就要使用super关键字
子类接口部分的成员变量不允许和父类接口部分的成员变量命名相同
子类定义的成员变量都可以与父类实现部分定义的成员变量同名。
当子类的实现部分定义了与父类实现部分同名的成员变量时,父类的成员变量就会被隐藏,子类方法很难直接访问到父类的成员变量,此时可通过调用父类的方法来访问父类中被隐藏的成员变量。
self关键字总是指向调用该方法的对象。self最大的作用是让类中的一个方法访问或调用该类中的另一个方法或成员变量
#import <Foundation/Foundation.h>
//父类的接口
@interface Bird : NSObject
- (void)fly;
@end
//父类的实现
@implementation Bird
- (void)fly {
NSLog(@"我是鸟,我会飞");
}
@end
//子类的接口
@interface Ostrich : Bird
- (void)fly;
- (void)birdfly;
@end
//子类的实现
@implementation Ostrich
- (void)fly {
NSLog(@"我是鸵鸟,我不会飞");
}
- (void)birdfly {
//这里用到了super关键字
[super fly];
}
@end
//主函数
int main() {
@autoreleasepool {
Bird *a = [[Bird alloc] init];
//父类的输出
[a fly];
Ostrich *b = [[Ostrich alloc] init];
//子类的输出
[b fly];
//在子类中调用父类被覆盖的方法
[b birdfly];
}
}
代码运行结果如下:
父类接口的定义:
#import <Foundation/Foundation.h>
@interface Parent:NSObject{
int _a;
}
@property(nonatomic,assign) int a;
@end;
父类接口的实现:
#import"0608.h"
@implementation Parent
-(id)init{
if(self=[super init]){
self->_a=5;
}
return self;
}
@end;
子类接口的定义:
#import<Foundation/Foundation.h>
#import"0608.h"
@interface Sub:Parent
-(void)accessOwner;
@end;
子类接口的实现:
#import<Foundation/Foundation.h>
#import"0607.h"
@implementation Sub{
int _a;
}
-(id)init{
if (self=[super init]){
self->_a=7;
}
return self;
}
-(void)accessOwner{
NSLog(@"zilei:%d",_a);
NSLog(@"fulei:%d",super.a);
}
@end;
测试:
#import <Foundation/Foundation.h>
#import"0607.h"
int main(){
@autoreleasepool {
Sub* sub=[[Sub alloc]init];
[sub accessOwner];
}
}
代码运行结果:
二、多态
1.什么时候会出现多态?
编译类型和运行类型不一致
OC指针类型的变量有两个:一个是编译时的类型,一个是运行时的类型。编译时的类型有声明该变量时使用的类型决定,运行时的类型有实际赋给该变量的对象决定。指针变量在编译时只能调用其编译时类型所具有的方法,但运行时执行它运行类型所具有的方法。
#import <Foundation/Foundation.h>
//父类的接口部分
@interface Base : NSObject
- (void)base;
- (void)test;
@end
//父类的接口的实现
@implementation Base
- (void)base {
NSLog(@"父类的base方法");
}
- (void)test {
NSLog(@"父类的将被覆盖的方法");
}
@end
//子类的接口部分
@interface Subclass : Base
- (void)sub;
@end
//子类的接口的实现
@implementation Subclass
- (void)test {
NSLog(@"子类覆盖父类的方法");
}
- (void)sub {
NSLog(@"子类的sub方法");
}
@end
int main() {
@autoreleasepool {
Base *a= [[Base alloc] init];
//父类a的定义时的类型和运行时的类型一致,不发生多态
[a base];
[a test];
Subclass *b = [[Subclass alloc] init];
//子类b的定义时的类型和运行时的类型一致,不发生多态
[b base];
[b test];
[b sub];
//定义了一个父类c但运行时的类型是子类
Base *ploymophic_c = [[Subclass alloc] init];
//C发生多态
[ploymophic_c base];
[ploymophic_c test];
id dynamic = ploymophic_c;
[dynamic sub];
}
}
三、指针变量的强制类型转换
1.指针变量的强制类型转换
(type)variable*
就是指把variable变量转化成为一个type类型的变量。
此处和C一样,不在多说。
需要注意的是:这个类型转换运算还可以将一个指针类型的变量转换成为其子类类型。
这种强制类型转换只是改变了该指针变量编译时的类型,但变量所指向的对象的实际类型并不会发生任何改变。
#import <Foundation/Foundation.h>
#import"0607.h"
int main(){
@autoreleasepool {
NSObject* obj=@"hello";
NSString* objStr=(NSString*)obj;
NSLog(@"%@",objStr);
NSObject* obj2=@"ios";
NSDate* date=(NSDate*)obj2;
//date实际指向的对象是NSString类型,所以在编译时会报错
NSLog(@"%d",[date isEqualToDate:[NSDate date]]);
}
}
注释报错的那行后:
2.判断指针变量的实际类型
判断指针变量实际指向的对象是否为某个类,某个子类的实例的方法
- -(BOOL)isMemberOfClass:clazz
判断该对象是否为clazz类的实例 - -(BOOL)isKindOfClass:clazz
判断该类是否为clazzl类或其子类的实例 - +(BOOL)isSubclassOfClass:clazz
这是个类方法,用于盘丢安当前类是否为clazz类的子类
上面的两个方法可以使用任何对象作为调用者,接着向该方法传入任何类,该方法将会返回一个BOOL类型的类,用于表明该对象实际指向对象的类型。
代码如下:
#import<Foundation/Foundation.h>
int main(int argc,char* arvv[]){
@autoreleasepool {
//hello声明时使用的是NSObject类
NSObject* hello=@"hello";
//NSObject是所有类的父类,因此返回1
NSLog(@"字符串是否为NSObject类的实例:%d",
([hello isKindOfClass:[NSObject class]]));
NSLog(@"字符串是否为NSString类的实例:%d",
([hello isKindOfClass:[NSString class]]));
NSLog(@"字符串是否是NSdate类的实例:%d",
([hello isKindOfClass:[NSDate class]]));
NSString* a=@"hello";
NSLog(@"a是否为NSDate类的实例:%d",
([a isKindOfClass:[NSDate class]]));
}
}
以下是代码运行结果: