美文网首页
iOS Runtime学习

iOS Runtime学习

作者: iOS_tree | 来源:发表于2019-03-22 15:11 被阅读0次

    iOS的Object-C是一门动态语言,在调用对象的方法时,是通过向对象发送消息的形式进行实现,在编译阶段并不会判断发送对象的方法是否存在,而是在代码运行的时候进行的判断。在正常情况下,调用一个对象的没有实现的方法,会导致程序发生崩溃消息,提示找不到该对象的实现方法。Object-C语言的动态特性基于Runtime系统实现,我们可以通过Runtime的一些公共的函数进行一些特殊操作。如给系统的类动态添加属性,调换类的实现方法等。我在这里简单介绍一些我们可以常用的Runtime方法。

    1、动态添加属性

    当我们需要给系统的类或者第三方库的类添加属性时,我们可以使用Runtime进行实现。主要通过两个方法进行关联属性的设置和获取,方法如下:

    //设置对象的关联属性
    objc_setAssociatedObject(id _Nonnull object, const void * _Nonnull key,
                             id _Nullable value, objc_AssociationPolicy policy)
    
    //获取对象的关联属性
    objc_getAssociatedObject(id _Nonnull object, const void * _Nonnull key)
    

    实例代码如下:

    - (void)setWeight:(float)weight {
        objc_setAssociatedObject(self, "weight", @(weight), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
    }
    
    - (float)getWeight {
        return [objc_getAssociatedObject(self, "weight") floatValue];
    }
    

    2、动态方法解析

    当我们的需求存在需要调用方法1,但是方法1又没有实现时,我们可以使用动态添加方法的方式进行实现。当消息发送的对象的方法没有实现时,系统会调用对象的+ (BOOL)resolveClassMethod:(SEL)sel方法或者+ (BOOL)resolveInstanceMethod:(SEL)sel方法进行处理,我们实现这个两个方法及可拦截方法的调用的第一步,在获取到我们需要动态添加的方法时,此时进行动态添加即可,当我们返回YES时,代表解决了方法不存在的文图,代码如下:

    void functionMethod1(id obj, SEL _cmd) {
        NSLog(@"Doing functionMethod1   %@",obj);
    }
    
    void classFunctionMethod1(id obj, SEL _cmd) {
        NSLog(@"Doing classFunctionMethod1   %@",obj);
    }
    
    /*********************1、动态方法解析******************************/
    //处理类方法
    + (BOOL)resolveClassMethod:(SEL)sel {
        if (sel == @selector(class_function1)) {
            class_addMethod(object_getClass(self), sel, (IMP)classFunctionMethod1, "v@:");
            return YES;
        }
        return [super resolveClassMethod:sel];
    }
    
    //处理实例方法
    + (BOOL)resolveInstanceMethod:(SEL)sel {
        if (sel == @selector(function1)) {
            class_addMethod([self class], sel, (IMP)functionMethod1, "v@:");
            return YES;
        }
        return [super resolveInstanceMethod:sel];
    }
    

    代码通过判断function1和class_function1方法来进行拦截,进把方法实现指向functionMethod1函数和classFunctionMethod1函数。此时会调用functionMethod1函数和classFunctionMethod1函数来替代function1和class_function1方法的实现。阻止了程序的崩溃。

    3、重定向消息接收者

    当我们调用某个对象的某个方法时,该方法又没有实现时,可以对该方法进行重新指定调用对象,也就是发送消息的对象主角。当方法消息发送给另外一个对象后,只要另外一个对象实现该方法,则程序会继续运行下去,以此避免因为不存在的方法而导致程序崩溃。代码如下:

    /*********************2、重定向消息接收者******************************/
    //处理类方法
    + (id)forwardingTargetForSelector:(SEL)aSelector {
        if (aSelector == @selector(class_function2)) {
            return [ESCPersonModel class];
        }
        return [super forwardingTargetForSelector:aSelector];
    }
    
    //处理实例方法
    - (id)forwardingTargetForSelector:(SEL)aSelector {
        if (aSelector == @selector(function2)) {
            return [ESCPersonModel new];
        }
        
        return [super forwardingTargetForSelector:aSelector];
    }
    

    4、重定向方法接收着和方法名

    当我们通过第3步的重定向消息接受者没有对消息进行处理时,会进入最后一步,再次确认消息及消息接收者,此时方法消息及消息接收者被封装成NSInvocation对象,我们更改NSInvocation对象下的消息签名及消息接收对象即可避免程序崩溃的产生。代码如下:

    /*********************3、重定向消息接收者和方法******************************/
    //处理类方法
    + (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
        if ([NSStringFromSelector(aSelector) isEqualToString:@"class_function3"]) {
            return [NSMethodSignature signatureWithObjCTypes:"v@:"];//签名,进入forwardInvocation
        }
        
        return [super methodSignatureForSelector:aSelector];
    }
    
    + (void)forwardInvocation:(NSInvocation *)anInvocation {
        SEL sel = anInvocation.selector;
        if (sel_isEqual(sel, NSSelectorFromString(@"class_function3"))) {
            anInvocation.selector = NSSelectorFromString(@"class_function2");
            Class p = [ESCPersonModel class];
            if([p respondsToSelector:anInvocation.selector]) {
                [anInvocation invokeWithTarget:p];
            }else {
                [self doesNotRecognizeSelector:sel];
            }
        }
    }
    
    //处理实例方法 
    - (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
        if ([NSStringFromSelector(aSelector) isEqualToString:@"function3"]) {
            return [NSMethodSignature signatureWithObjCTypes:"v@:"];//签名,进入forwardInvocation
        }
        
        return [super methodSignatureForSelector:aSelector];
    }
    
    - (void)forwardInvocation:(NSInvocation *)anInvocation {
        SEL sel = anInvocation.selector;
        if (sel_isEqual(sel, NSSelectorFromString(@"function3"))) {
            anInvocation.selector = NSSelectorFromString(@"function2");
            ESCPersonModel *p = [ESCPersonModel new];
            if([p respondsToSelector:anInvocation.selector]) {
                [anInvocation invokeWithTarget:p];
            }else {
                [self doesNotRecognizeSelector:sel];
            }
        }
    }
    

    demo地址:https://github.com/XMSECODE/ESCRuntimeDemo
    runtime详解参考:https://www.jianshu.com/p/d4b55dae9a0d

    相关文章

      网友评论

          本文标题:iOS Runtime学习

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