美文网首页
Runtime运行时二:方法

Runtime运行时二:方法

作者: Carson_Zhu | 来源:发表于2018-02-02 12:54 被阅读9次

    SEL

    SEL又叫选择器,是表示一个方法的selector的指针,其定义如下:

    typedef struct objc_selector *SEL;
    

    方法的selector用于表示运行时方法的名字。Objective-C在编译时,会依据每一个方法的名字、参数序列,生成一个唯一的整型标识(Int类型的地址),这个标识就是SEL。两个类之间,只要方法名相同,那么方法的SEL就是一样的,每一个方法都对应着一个SEL。所以在Objective-C同一个类(及类的继承体系)中,不能存在2个同名的方法,即使参数类型不同也不行。

    方法调用
    [self testMethod:@"数据"];
    [self performSelector:@selector(testMethod:) withObject:@"数据"];
    // Runtime
    objc_msgSend(self, @selector(testMethod:), @"数据");
    

    iOS8之后报错

    // 用函数指针,指向了runtime的objc_msgSend方法才能调用,不能直接调用objc_msgSend()
    void (*runtime_msgSend)(id self, SEL _cmd, id param) = (void *)objc_msgSend;
    
    runtime_msgSend(self.methodHelper, NSSelectorFromString(@"testMethod:"), @"hello world");
    
    动态决议

    对象在接收到未知的消息时,首先会调用所属类的类方法。我们为该未知消息新增一个“处理方法”,通过运行时class_addMethod函数动态添加到类里面就可以了。

    // 接收处理未知实例方法
    + (BOOL)resolveInstanceMethod:(SEL)sel {  
        if ([NSStringFromSelector(sel) isEqualToString:@"testMethod:"]) {
            if (!class_addMethod([self class], sel, (IMP)testMethodImplementation, "v@:@")
                ) {
                NSLog(@"动态添加方法失败");
            }
        }
        return [super resolveInstanceMethod:sel];
    }
    
    // 接收处理未知类方法
    + (BOOL)resolveClassMethod:(SEL)sel {
        // 动态添加类方法
        return [super resolveClassMethod:sel];
    }
    

    必须要有动态添加方法的实现

    void testMethodImplementation(id self, SEL _cmd, id param) {
        NSLog(@"这是动态创建的方法的实现");
    }
    

    适用场景:
    使用关键字@dynamic在类的实现文件中修饰一个属性,表明我们会为这个属性动态提供存取方法,编译器不会再默认为我们生成这个属性的settergetter方法了,需要我们自己提供。

    消息转发
    • 第一种方式:
    - (id)forwardingTargetForSelector:(SEL)aSelector {    
        if ([self.methodHelper respondsToSelector:aSelector]) {
            return self.methodHelper;
        }
        return [super forwardingTargetForSelector:aSelector];
    }
    
    • 第二种方式:
    - (void)forwardInvocation:(NSInvocation *)anInvocation {
        if ([self.methodHelper respondsToSelector:anInvocation.selector]) {
            [anInvocation invokeWithTarget:self.methodHelper];
        }
    }
    
    方法交换

    当系统类的方法不能满足我们的需求,比如你想在所有的UIViewControllerviewWillAppear里设置背景颜色,你可以写一个Base类,然后去继承。你也可以选择Runtime的方法交换。

    • 第一步
      导入ObjectiveC
    @import ObjectiveC;
    
    • 第二步
      创建一个UIViewController的Category,实现+ (void)load方法
    + (void)load {
        // class_getInstanceMethod 获取对象方法
        // class_getClassMethod    获取类方法
        
        // 获取系统的viewWillAppear方法
        Method viewWillAppearMethod = class_getInstanceMethod([UIViewController class], @selector(viewWillAppear:));
        // 获取自定义的bgViewWillAppear方法
        Method bgViewWillAppearMethod = class_getInstanceMethod([UIViewController class], @selector(bgViewWillAppear:));
        // 交换方法实现
        method_exchangeImplementations(viewWillAppearMethod, bgViewWillAppearMethod);
    }
    
    • 第三步
      实现自定义的方法,并调用(因为方法交换了,实际上调用的是系统的方法)
    - (void)bgViewWillAppear:(BOOL)animated {
        self.view.backgroundColor = [UIColor redColor];
        [self bgViewWillAppear:animated];
    }
    

    相关文章

      网友评论

          本文标题:Runtime运行时二:方法

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