动态多态

来源:互联网 发布:网络需求分析报告 编辑:程序博客网 时间:2024/06/03 18:19

多态:相同接口,不同实现
相同接口:方法的签名、参数、返回值相同
不同实现:具体实现的内容不同

动态多态包括:

  1. 动态类型识别
  2. 动态绑定
  3. 动态加载

动态类型识别

@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指针的信息传递过程就是从子类开始,一个一个往上一级寻找是否有匹配的信息,把对象和类对象匹配起来,从而实现消息的传递过程。
    isa的消息传递

类对象的方法链表

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即为SELfor (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!"];         }    }
0 0