Runtime

作者: cghneany | 来源:发表于2017-07-11 11:02 被阅读0次

    C语言中,在编译期,函数的调用就会决定调用哪个函数,而OC的函数,属于动态调用过程,在编译期并不能决定真正调用哪个函数,只有在真正运行时才会根据函数的名称找到对应的函数来调用,我们写的代码在程序运行过程中都会被转化成runtime的C代码执行,例如[target doSomething];会被转化成objc_msgSend(target, @selector(doSomething)),调用方法其实就是给对象发送消息。

    一、对象、类、元类

    OC中一切都被设计成了对象,一个类被初始化成一个实例,这个实例是一个对象。objc_class继承于objc_object,所以类也是一个对象。

    typedef  struct  objc_class    *Class;

    typedef  struct  objc_object  *id;

    @interface Object{

    Class isa;

    }

    @interface NSObject {

    Class isa OBJC_ISA_AVAILABILITY;

    }

    structobjc_object {

    private:

    isa_t isa;

    }

    struct  objc_class : objc_object {

    // Class ISA;

    Class superclass;

    cache_t cache;//

    formerly cache pointer and vtable

    class_data_bits_t bits;// class_rw_t * plus custom

    rr/alloc flags

    }

    unionisa_t

    {

    isa_t() { }

    isa_t(uintptr_t value) : bits(value) { }

    Class cls;

    uintptr_t bits;

    }

    当一个对象的实例方法被调用的时候,会通过isa找到相应的类,然后在该类的class_data_bits_t中去查找方法。class_data_bits_t是指向了类对象的数据区域。在该数据区域内查找相应方法的对应实现

    类对象的类方法调用时,通过类的isa在元类中获取方法的实现。

    meta-class存储着一个类的所有类方法。每个类都会有一个单独的meta-class,因为每个类的类方法基本不可能完全相同。

    类、对象、元类之间的关系

    实线是super_class指针,虚线是isa指针

    Root class (class)其实就是NSObject,NSObject是没有超类的,所以Root class(class)的superclass指向nil。

    每个Class都有一个isa指针指向唯一的Meta class

    Root class(meta)的superclass指向Root class(class),也就是NSObject,形成一个回路

    每个Meta class的isa指针都指向Root class (meta)

    二、获取类的信息

    //获取属性列表class_copyPropertyList([self class], &count);

    //获取方法列表

    class_copyMethodList([self class], &count);

    //获取成员变量列表

    class_copyIvarList([self class], &count);

    //获取协议列表

    class_copyProtocolList([self class], &count);

    1、自动归档解档

    获取类的成员变量列表,把变量名称当做key值进行归档解档

    2、字典转模型

    获取模型类的属性列表,根据属性字符串去字典里边取值,利用KVC赋值

    三、关联对象

    分类不能添加属性,因为添加了@property之后并不会自动帮我们生成实例变量以及存取方法,我们可以通过关联对象来给分类添加属性

    - (void)setAssociatedObject:(id)associatedObject {

    objc_setAssociatedObject(self,AssociatedKey, associatedObject, OBJC_ASSOCIATION_RETAIN_NONATOMIC);

    }

    - (id)associatedObject {

    returnobjc_getAssociatedObject(self, AssociatedKey);

    }

    objc_setAssociatedObject(id object, const void *key,id value, objc_AssociationPolicy policy)

    id object给谁设置关联对象

    const void *key关联对象唯一的key,获取时会用到

    id value关联对象

    objc_AssociationPolicy关联策略

    四、动态加载方法

    我们知道发送消息是通过objc_msgSend(id, SEL, ...)来实现的,首先通过对象的isa指针找到对象所在的类,在Class中先去cache中通过SEL查找对应函数method,若cache中未找到,再去methodList中查找,若methodlist中未找到,则取superClass中查找。若能找到,则将method加入到cache中,以方便下次查找,并通过method中的函数指针跳转到对应的函数中去执行。若是一直查找到根类还是没有找到,就会执行resolveInstanceMethod方法。在这个方法里面,我们可以动态的给类对象或者实例对象动态的增加方法。

    1、动态添加实例方法

    void dynamicAddressMethod(id self,SEL _cmd) {

    NSLog(@"Address --->百子湾");

    }

    + (BOOL)resolveInstanceMethod:(SEL)sel{

    if(sel ==@selector(address)){

    class_addMethod([self class], sel, (IMP)dynamicAddressMethod,"v@:");

    returnYES;

    }

    return[super resolveInstanceMethod:sel];

    }

    2、动态添加类方法

    void dynamicNameMethod(id self,SEL sel,NSString *n) {

    NSLog(@"Name ---> %@",n);

    }

    + (BOOL)resolveClassMethod:(SEL)sel {

    //方法1添加到当前类的元类

    ClassmetaClass = objc_getMetaClass(class_getName(self));

    if (sel ==@selector(name)) {

    class_addMethod(metaClass,sel, (IMP)dynamicNameMethod, "v@:");

    returnYES;

    }

    //方法2添加到NSObject类的元类

    //ClassmetaClass = objc_getMetaClass(object_getClassName((id)[NSObject class]));

    //if (sel== @selector(name)) {

    //class_addMethod(metaClass,sel, (IMP)dynamicNameMethod, "v@:@");

    //return YES;

    //}

    ////方法3添加到NSObject

    //if (sel== @selector(name)) {

    //class_addMethod([NSObject class], sel, (IMP)dynamicNameMethod,"v@:@");

    //return YES;

    //}

    //

    return[super resolveClassMethod:sel];

    }

    六、消息转发

    消息转发

    向一个对象发送其无法处理的消息,如果我们在resolve...Method方法里边添加方法,就会执行消息转发

    forwardingTargetForSelector可以返回一个可以执行方法的对象

    - (id)forwardingTargetForSelector:(SEL)aSelector {

    if(aSelector == @selector(run)){

    return[RunMethod new];

    }

    return[super forwardingTargetForSelector:aSelector];

    }

    如果我们不实现forwardingTargetForSelector,就会调用methodSignatureForSelector和forwardInvocation

    - (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector{

    NSLog(@"对象方法方法签名");

    if(aSelector == @selector(run)) {

    return[NSMethodSignature signatureWithObjCTypes:"v@:"];

    }

    return[super methodSignatureForSelector:aSelector];

    }

    关于生成签名的类型"v@:",每一个方法会默认隐藏两个参数,self、_cmd,self代表方法调用者,_cmd代表这个方法的SEL,签名类型就是用来描述这个方法的返回值、参数的,v代表返回值为void,@表示self,:表示_cmd。

    - (void)forwardInvocation:(NSInvocation*)anInvocation {

    //自定义转发逻辑1

    SEL sel =anInvocation.selector;

    RunMethod*runM = [RunMethod new];

    if ([runM respondsToSelector:sel]){

    [anInvocation invokeWithTarget:runM];

    }else {

    [superforwardInvocation:anInvocation];

    }

    ////自定义转发逻辑2

    ////在这里可以改变方法选择器

    //[anInvocation setSelector:@selector(unknown)];

    ////改变方法选择器后,需要指定消息的接收者

    //[anInvocation invokeWithTarget:self];

    //[anInvocation setSelector:@selector(run)];

    //[anInvocation invokeWithTarget:[RunMethod new]];

    }

    七、方法交换

    Method one = class_getInstanceMethod([Method_1

    class], @selector(eat));

    Method two = class_getInstanceMethod([Mehtod_2class], @selector(eat));

    method_exchangeImplementations(one, two);

    Method_1 *onem = [[Method_1 alloc]init];

    [onem eat];

    Mehtod_2 *twom = [[Mehtod_2 alloc]init];

    [twom eat];

    页面统计:

    @implementation UIViewController (statistics)

    +load会在类初始加载时调用,+initialize方法是以懒加载的方式被调用的,如果程序一直没有给某个类或它的子类发送消息,那么这个类的+initialize方法是永远不会被调用的

    + (void)load {

    MethodoriginalMethod = class_getInstanceMethod([self class],@selector(viewWillAppear:));

    Method swizzledMethod= class_getInstanceMethod([self class], @selector(statistics_viewWillAppear:));

    method_exchangeImplementations(originalMethod, swizzledMethod);

    }

    - (void)statistics_viewWillAppear:(BOOL)animated {

    NSString*pageName = NSStringFromClass([self class]);

    NSLog(@"pageName = %@",pageName);

    [selfstatistics_viewWillAppear:animated];

    }

    - (void)statistics_viewWillDisappear:(BOOL)animated {

    [selfstatistics_viewWillDisappear:animated];

    }

    @end

    相关文章

      网友评论

          本文标题:Runtime

          本文链接:https://www.haomeiwen.com/subject/sqyjhxtx.html