美文网首页
ios-runtime添加方法

ios-runtime添加方法

作者: 命运建筑师fly | 来源:发表于2018-10-23 08:03 被阅读0次

    runtime官方
    Objective-C Runtime Programming Guide - Dynamic Method Resolution

    1、添加方法
    • 开发使用场景:如果一个类方法非常多,加载类到内存的时候也比较耗费资源,需要给每个方法生成映射表,可以使用动态给某个类,添加方法解决。
    • 经典面试题:有没有使用performSelector,其实主要想问你有没有动态添加过方法
      方法:
      OBJC_EXPORT BOOL class_addMethod(Class cls, SEL name, IMP imp,
      const char *types)
    + (BOOL)resolveInstanceMethod:(SEL)se//动态对象方法
    + (BOOL)resolveClassMethod:(SEL)sel;//动态类方法
    通过重写上面方法,调用class_addMethod();函数来动态添加方法,同时返回YES即可,如果返回NO,则会进入消息转发机制.
    

    写法一:

    //可以根据这个方法来取到
    
    - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
        [self performSelector:@selector(drive)];
    }
    
    void startEngine(id self, SEL _cmd, NSString *brand) { 
        NSLog(@"my %@ car starts the engine", brand);
    }
    
    + (BOOL)resolveInstanceMethod:(SEL)sel { 
        if (sel == @selector(drive)) { 
            class_addMethod([self class], sel, (IMP)startEngine, "v@:@"); 
            return YES; 
        } 
        return [super resolveInstanceMethod:sel];
    }
    
    

    写法二:使用oc的方法

    //使用这个来调用
    
    - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
        [self performSelector:@selector(drive)];
    }
    
    + (BOOL)resolveInstanceMethod:(SEL)sel { 
        if (sel == @selector(drive)) { 
    ////获取类中的某个实例方法(减号方法)
    Method exchangeM = class_getInstanceMethod([self class], @selector(startEngine:));
            class_addMethod([self class], sel, class_getMethodImplementation(self, @selector(startEngine:)), method_getTypeEncoding(exchangeM));
    //这样就拿到了eatWithPersonName方法的type encodings 用作class_addMethod的第四个参数
            return YES; 
        } 
        return [super resolveInstanceMethod:sel];
    }
    
    - (void)startEngine:(NSString *)brand { 
        NSLog(@"my %@ car starts the engine", brand);
    }
    
    其中 class_getMethodImplementation 意思就是获取 SEL 的具体实现的指针(拿到方法eatWithPersonName的IMP指针)
    然后创建一个新的类「DynamicSelector」,在这个新类中我们实现对「Car」的动态添加方法
    
    

    参数解释:
    cls 参数表示需要添加新方法的类
    name 参数表示 selector 的方法名称
    imp 即 implementation ,表示由编译器生成的、指向实现方法的指针。也就是说,这个指针指向的方法就是我们要添加的方法
    最后一个参数 *types 表示我们要添加的方法的返回值和参数
    char *types
    比如:”v@:”意思就是这已是一个void类型的方法,没有参数传入。

    再比如 “i@:”就是说这是一个int类型的方法,没有参数传入。

    再再比如”i@:@”就是说这是一个int类型的方法,又一个参数传入

    performSelector: withObject:是在iOS中的一种方法调用方式。他可以向一个对象传递任何消息,而不需要在编译的时候声明这些方法。
    所以这也是runtime的一种应用方式.所以performSelector和直接调用方法的区别就在与runtime。直接调用编译是会自动校验。如果方法不存在,那么直接调用 在编译时候就能够发现,编译器会直接报错。
    但是使用performSelector的话一定是在运行时候才能发现,如果此方法不存在就会崩溃。所以如果使用performSelector的话他就会有个最佳伴侣- (BOOL)respondsToSelector:(SEL)aSelector;来在运行时判断对象是否响应此方法

    method相关的方法

    //判断类中是否包含某个方法的实现
      BOOL class_respondsToSelector(Class cls, SEL sel)
      //获取类中的方法列表
      Method *class_copyMethodList(Class cls, unsigned int *outCount) 
      //为类添加新的方法,如果方法该方法已存在则返回NO
      BOOL class_addMethod(Class cls, SEL name, IMP imp, const char *types)
      //替换类中已有方法的实现,如果该方法不存在添加该方法
      IMP class_replaceMethod(Class cls, SEL name, IMP imp, const char *types) 
      //获取类中的某个实例方法(减号方法)
      Method class_getInstanceMethod(Class cls, SEL name)
      //获取类中的某个类方法(加号方法)
      Method class_getClassMethod(Class cls, SEL name)
      //获取类中的方法实现
      IMP class_getMethodImplementation(Class cls, SEL name)
      //获取类中的方法的实现,该方法的返回值类型为struct
      IMP class_getMethodImplementation_stret(Class cls, SEL name) 
    
      //获取Method中的SEL
      SEL method_getName(Method m) 
      //获取Method中的IMP
      IMP method_getImplementation(Method m)
      //获取方法的Type字符串(包含参数类型和返回值类型)
      const char *method_getTypeEncoding(Method m) 
      //获取参数个数
      unsigned int method_getNumberOfArguments(Method m)
      //获取返回值类型字符串
      char *method_copyReturnType(Method m)
      //获取方法中第n个参数的Type
      char *method_copyArgumentType(Method m, unsigned int index)
      //获取Method的描述
      struct objc_method_description *method_getDescription(Method m)
      //设置Method的IMP
      IMP method_setImplementation(Method m, IMP imp) 
      //替换Method
      void method_exchangeImplementations(Method m1, Method m2)
    
      //获取SEL的名称
      const char *sel_getName(SEL sel)
      //注册一个SEL
      SEL sel_registerName(const char *str)
      //判断两个SEL对象是否相同
      BOOL sel_isEqual(SEL lhs, SEL rhs) 
    
      //通过块创建函数指针,block的形式为^ReturnType(id self,参数,...)
      IMP imp_implementationWithBlock(id block)
      //获取IMP中的block
      id imp_getBlock(IMP anImp)
      //移出IMP中的block
      BOOL imp_removeBlock(IMP anImp)
    
      //调用target对象的sel方法
      id objc_msgSend(id target, SEL sel, 参数列表...)
    

    实际使用

    
    @implementation ViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        // Do any additional setup after loading the view, typically from a nib.
    
        Person *p = [[Person alloc] init];
    
        // 默认person,没有实现eat方法,可以通过performSelector调用,但是会报错。
        // 动态添加方法就不会报错
        [p performSelector:@selector(eat)];
    
    }
    
    
    @end
    
    
    @implementation Person
    // void(*)()
    // 默认方法都有两个隐式参数,
    void eat(id self,SEL sel)
    {
        NSLog(@"%@ %@",self,NSStringFromSelector(sel));
    }
    
    // 当一个对象调用未实现的方法,会调用这个方法处理,并且会把对应的方法列表传过来.
    // 刚好可以用来判断,未实现的方法是不是我们想要动态添加的方法
    + (BOOL)resolveInstanceMethod:(SEL)sel
    {
    
        if (sel == @selector(eat)) {
            // 动态添加eat方法
    
            // 第一个参数:给哪个类添加方法
            // 第二个参数:添加方法的方法编号
            // 第三个参数:添加方法的函数实现(函数地址)
            // 第四个参数:函数的类型,(返回值+参数类型) v:void @:对象->self :表示SEL->_cmd
            class_addMethod(self, @selector(eat), eat, "v@:");
    
        }
    
        return [super resolveInstanceMethod:sel];
    }
    @end
    

    备注:有些文字来自网友的帖子,本帖用于记录学习过程

    相关文章

      网友评论

          本文标题:ios-runtime添加方法

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