多态性
OC的指针类型的变量有俩个:一个是编译时的类型,一个是运行时的类型,编译时的类型由声明该变量时使用的类型决定,运行时的类型由实际赋给该变量的对象决定。如果编译时类型和运行时类型完全不一致,就可能出现所谓的多态——来自疯狂iOS讲义
#import <Foundation/Foundation.h>
@interface FKBase: NSObject
- (void) base;
- (void) test;
@end
下面定义一个类来继承上面的类?
#import <Foundation/Foundation.h>
#import "FKBase.h"
@interface FKSubclass : FKBase
- (void) sub;
@end
第一个类的实现部分⬇️
#import "FKBase.h"
@implementation FKBase
- (void) base {
NSLog(@"普通方法");
}
- (void) test {
NSLog(@"父类的将被覆盖的test方法");
}
@end
第二个类的实现部分⬇️
#import <Foundation/Foundation.h>
#import "FKSubclass.h"
@implementation FKSubclass
- (void) test {
NSLog(@"子类覆盖父类的test的方法");
}
- (void) sub {
NSLog(@"子类的sub方法");
}
@end
int main(int argc, char * argv[]) {
@autoreleasepool {
FKBase * bc = [[FKBase alloc] init];
[bc base];
[bc test];
// 下面编译时e类型和运行时类型完全一样,因此不存在多态
FKSubclass * sc = [[FKSubclass alloc] init];
// 下面调用将执行从父类继承到的base方法
[sc base];
// 下面调用将执行子类重写的test方法
[sc test];
// 下面调用将执行子类定义的sub方法
[sc sub];
// 下面编译时类型和运行时类型不一样,发生多态
FKBase * sb = [[FKSubclass alloc] init];
// 下面调用从FKBase继承得到的base方法
[sb base];
// 下面将调用执行子类重写的test方法
[sb test];
// 因为sb的编译类型时FKBase
// FKBase类没有提供sub方法,所以[sb sub]会报错
//可以将任何类型的指针变量赋值给id类型
id sb2 = sb;
[sb2 sub];
}
}
以上前一部分定义的指针变量与创建的对象类型一致(bc和sc),第三个指针变量sb,编译时类型为FKBase,而运行时却是FKSubclass,当调用test方法时,执行了FKSubclass里重写的test方法。
- 子类是特殊的父类,OC允许把一个子类对象直接赋给一个父类的指针变量,无需类型转换。
- 当一个子类对象直接赋给父类指针时,例如FKBase * sb = [[FKSubclass alloc]init];这个sb变量在编译时类型还是FKBase,在运行时变成了FKSubclass,当运行时调用该指针变量的方法时,其方法总是表现子类方法也就是调用子类方法,这就可能出现相同类型的变量调用同一个方法出现不同的行为特征,这就时多态
- 指针变量在编译阶段指针调用其编译类型所具有的方法,但运行时则执行运行时类型所具有的方法
指针变量的强制类型转换
- 类型转换运算符是圆括号(),用法:
(type *)variable
将variable转换成一个type类型的变量
2.这种强制转换只是改变了指针变量的编译时类型,但是该变量所指对象的实际类型不会发生改变
#import <Foundation/Foundation.h>
#import "FKSubclass.h"
int main(int argc, char * argv[]) {
@autoreleasepool {
NSObject * obj = @"Hello";
//由于objd变量所指的对象是NSString对象,所以运行时也可以通过。
NSString * objStr = (NSString *)obj;
NSLog(@"%@", objStr);
// 定义一个obj2变量,编译类型为NSObject,实际类型为NSString
NSObject * obj2 = @"iOS";
//尝试将obj2强制转换为NSDate,这行代码没有问题
//但程序只是定义了一个NSDate类型的指针,该指针与obj2指向同一个对象
NSData * date = (NSData *) obj2;
// 程序调用date的isEqualToDate:方法
//由于date的编译类型是NSDate,因此编译时没有任何问题
//由于date实际指向的对象时NSString,因此程序执行时就会引发错误
NSLog(@"%d", [date isEqualToDate:[NSDate date]]);
}
}
当把子类赋给父类对象时,被称为向上转型,这种转型总是可以成功的,这也从另一个侧面证实了子类是一种特殊的父类。这种转型只是表明这个指针变量的编译类型是父类,但实际执行它的方法时,依然子类对象的行为方式