多态:相同接口,不同实现
相同接口:方法的签名、参数、返回值相同
不同实现:具体实现的内容不同
动态多态包括:
- 动态类型识别
- 动态绑定
- 动态加载
动态类型识别
@interface A:NSObject
-(void)draw;
@end
@interface B:A
-(void)draw;
@end
@interface C:B
-(void)draw;
@end
@interface D:NSObject
-(void)paint:(A*)aA;
//此处可改为
//-(void)paint:(id)aA
@end
@implementation D
-(void)paint:(A*)aA
//此处可改为
//-(void)paint:(id)aA
{[aA draw];}
@end
@autoreleasepool{
D *d = [[D alloc] init];
A *a = [[D alloc] init];
B *b = [[D alloc] init];
C *c = [[D alloc] init];
[d paint:a];
[d paint:b];
[d paint:c];
//因为b,c对象都继承于A类,故在此可以用父类指针调用子类对象
id类型和多态的识别原理
- 而这里如果改成id指针类型,其不进行类型检查,可以指向属于任何类的对象,因为id类型不能说明对象的类型,而是要求对象本身提供这些信息
- isa实例变量永远是对象的第一个实例对象, 而id指针相当于class *isa 指针,其中包含了可以储存对象各种信息的地址,在信息的传递过程中充当一个“桥梁”的作用。
- id指针的信息传递过程就是从子类开始,一个一个往上一级寻找是否有匹配的信息,把对象和类对象匹配起来,从而实现消息的传递过程。
类对象的方法链表
struct objc_method{
SEL method_name;//方法ID
char* method_types;//方法类型(返回值、参数)
IMP method_imp;//方法地址(IMP)函数调用
SEL IMP类型的作用和特点
- SEL类型:selector,方法签名相同则ID相同,与类无关
- IMP类型:implementation, 是一个指针类型,寻找到函数过后的函数调用,返回一个id
typedef id(*IMP)(id, SEL,...)
@autoreleasepool{
Person *a = [[Person alloc] init];
[a print];
SEL act = @selector("print");//act得到print的SEL
//或者 SEL act = NSSelectorFromString(@"print");从字符串获得方法的SEL
//或者NSSring * name = NSStringFromSelector(act);从
const char * sn = sel_getName(act);
//用SEL做参数,找到指定的方法,并取得其首地址
a.name = @"tom";
NSLog(@"%s",sn);
IMP p = [a methodForSelector:act];
p(a, act);//p就相当于*IMP【typedef id(*IMP)】(id, SEL,...),a相当于self,act即为SEL
for (int i=0; i<10000; i++){
//[a print];
p(a, act);
}//在循环次数很多的情况下,如果用第一种方法,消息传递的方法名寻找过程很耗费时间,所以如果改进为第二种方法,可以节省很多时间
}
动态类型识别的常用方法
如何得到类对象
Class rectClass=[Rectangle class];
//通过类名得到类对象
Class aClass=[anObject class];
//通过实例得到类对象
if([obj1 class] == [obj2 class])
//两个类对象是否为同类
类(对象)和字符串
Class someClass = NSClassFromString(@"NSView");
id object = [[someClass alloc]init];
//从字符串得到类对象
NSString *view = NSStringFromClass([NSView class]);
NSString *className = NSStringFromClass([anObject class]}
//从类或者类对象得到类名的字符串
判定与应用
判定类与成员
class-object类对象的写法:[类名或者对象名 class]
-(BOOL)isKindOf:class-object
//对象是不是class-object或者其子类的成员
-(BOOL)isMemberOfClass:class-object
//对像是不是class-object的成员
if([object isMemberOf:[someClass class]])
//判定是否为某个类的实例对象
+(BOOL)isSubclassOfClass:class-object
//对象是指定类的子类吗
是否响应
等号右边selector的写法:@selector(方法名、@“字符串”)
-(BOOL)respondToSelector:selector
//对象是否能响应selector所指定的方法
+(BOOL)instancesRespondToSelector:selector
//指定的类对象是否能响应selector
应用
-(id)performSelector:selector
//应用selector指定的方法
-(id)performSelector:selector withObject:object
//应用selector指定的方法,传递参数object1
-(id)performSelector:selector withObject:object withObject:object2
//应用selector指定的方法,传递参数object1,传递参数object2
..........
静态/动态类型识别的区别
- 静态多态在编译之前就提供了对象信息,编译时可以检查(对象和类是否匹配,对象是否拥有指定的方法等)
- 动态多态是在编译时才确定对象信息,动态多态要求方法签名、返回值、参数类型完全一致
- 如果不涉及到多态,尽量使用静态类型
动态绑定
消息派发:oc中消息一直到运行时才能绑定到对应的方法
[receiver message];转换成函数
objc_msgSend(receiver, selector);
其中receivier赋值给self, selector就是方法选择器
如果消息是
[super message];
为了加快消息处理,运行时会缓存映射表,在每个类中查找方法会先在缓存中找,如果找不到再到方法映射表中去找
动态加载
oc允许在运行时加载一个新类,或给已有的类加载新的方法
只需要四步
1. #import 《objc/runtime.h>
2. 为class pair分配空间
3. 增加方法(class_addMethod)或者实例变量(class_addlvar)
4. 注册新类
#import<objc/runtime.h>//注意引入
void sayHello(id self,SEL_cnd,NSString* aHello){
NSLog(@"%@",aHello);
}
//-(void)sayHello:(NSString*)aHello;上面函数就时这个方法的等价写法
//void(*)(id self, SEL_cnd,NSString*);可以看出每个方法都有self和SEL两个隐含参数
@autoreleasepool{
Class parentClass=[NSObject class];
Class newClass=objc_allocateClassPair(parentClass,"ASNewClass",0);//为class pair分配空间
Class_addMethod(newClass,@selector(sayHello:),(IMP)sayHello,"v@:@");
//增加方法(class_addMethod)或者实例变量(class_addlvar)
objc_registerClassPair(newClass);//注册新类
id p = [[newClass alloc] init];
if([p respondsToSelector:@selector(sayHello:)]){
[p performSelector:@selector(sayHello:) withObject:@"hello world!"];
}
}