Runtime--Selector、IMP、Method

作者: 好_快 | 来源:发表于2016-09-21 18:20 被阅读598次

    Selector

    Selector的类型是SEL。能够用来唯一标识方法。能够像动态的函数指针一样精准的指向方法的implementation。

    获取方法

    在编译期,使用编译器指令@@selector,例如

    SEL aSelector = @selector(methodName);
    

    在运行期,使用NSSelectorFromString函数,例如

    SEL aSelector = NSSelectorFromString(@"methodName");
    

    使用方法

    一下方法在NSObject类中声明

    - (id)performSelector:(SEL)aSelector;
    - (id)performSelector:(SEL)aSelector withObject:(id)object;
    - (id)performSelector:(SEL)aSelector withObject:(id)object1 withObject:(id)object2;
    

    与Runtime

    首先定义一个方法

    -(void)getMethod:(NSString *)name
    {
        NSLog(@"getMethod:%@",name);
    }
    

    然后获取方法

    SEL sel = @selector(getMethod:);
    SEL sel2 = NSSelectorFromString(@"getMethod:");
    

    获取方法名称

     //获取名称
    const char *sel_name = sel_getName(sel);
    NSLog(@"SEL sel_name:%@",[NSString stringWithUTF8String:sel_name]);
    
    //输出
    SEL sel_name:ivarMethod
    

    注册方法

    //在添加一个Method之前需要先注册一个;如果方法已注册直接返回
    SEL sel_registerName(const char *str)
    
    //或者
    SEL sel_getUid(const char *str)
    

    例如

    //注册
    SEL sel_regist = sel_registerName("sel_regist");
    NSLog(@"SEL sel_regist:%@",NSStringFromSelector(sel_regist));
    
    SEL sel_regist2 =sel_getUid("sel_regist");
    NSLog(@"SEL sel_regist2:%@",NSStringFromSelector(sel_regist2));
    
    //输出
    SEL sel_regist:sel_regist
    SEL sel_regist2:sel_regist
    

    比较方法

    //相等于符号 == 
    BOOL sel_isEqual(SEL lhs, SEL rhs) 
    

    例如

    //比较
    BOOL isEqual = sel_isEqual(sel_regist2, sel_regist2);
    NSLog(@"SEL isEqual:%d",isEqual);
      
    //输出
    SEL isEqual:1    
    

    IMP(Implementation)

    一个指向实现Method的函数的开始的指针。此函数使用标准的C语言调用习惯来实现CGU架构。其实参数:self、selector、其它Method参数。其中实例方法而言,self指向对象的内存地址;对于类方法而言self指向其metaclass。

    Method

    此处讨论Method与Runtime相关的指示。首先定义一个简单的Data类,声明并实现一些方法

    @interface Data : NSObject
    
    -(void)getMethod:(NSString *)name;
    -(void)ivarMethod;
    +(void)classMethod;
    
    @end
    
    
    @implementation Data
    -(void)ivarMethod
    {
        NSLog(@"调用ivarMethod");
    }
    +(void)classMethod
    {
        NSLog(@"调用classMethod");
    
    }
    
    -(void)getMethod:(NSString *)name
    {
        NSLog(@"getMethod:%@",name);
    }
    @end
    

    获取Method

    注释:不管在header文件还是implement文件声明实现的方法均可以获取到。

    获取实例Method

     Method ivar_Method = class_getInstanceMethod([Data class], @selector(ivarMethod));
     SEL sel_ivar_Method = method_getName(ivar_Method);
     NSLog(@"Method class_getInstanceMethod:%@",NSStringFromSelector(sel_ivar_Method));
    
    //输出
    class_getInstanceMethod:ivarMethod
    

    获取类Method

    /* 获取类方法 */
    Method class_Method = class_getClassMethod([Data class], @selector(classMethod));
    SEL sel_class_Method = method_getName(class_Method);
    NSLog(@"Method class_getClassMethod:%@",NSStringFromSelector(sel_class_Method));
    
    //输出
    Method class_getClassMethod:classMethod    
    

    获取实例Method列表

    /** 
     * 获取类的实例方法列表
     * 
     * @return 以数组想法返回实例方法列表(需要free);只返回类本身实现的方法,不包括父类的继承的方法。 
     *   
     * 获取类方法列表: class_copyMethodList(object_getClass(cls), &count).
     * 获取父类方法:class_getInstanceMethod 或 class_getClassMethod.
     */
    Method *class_copyMethodList(Class cls, unsigned int *outCount);
    
    /* 获取实例方法列表 */
    unsigned int count_method = 0;
    Method *method_list = class_copyMethodList([Data class], &count_method);
    for (int i = 0; i < count_method; i ++)
    {
       Method method = method_list[i];
       SEL sel_Method = method_getName(method);
       NSLog(@"Method class_copyMethodList:%@",NSStringFromSelector(sel_Method));
    
    }
    
    //输出
    Method class_copyMethodList:ivarMethod
    Method class_copyMethodList:getMethod:
    

    获取类Method列表

    /* 获取实例方法列表 */
    unsigned int count_method = 0;
    Method *method_list = class_copyMethodList(object_getClass([Data class]), &count_method);
    for (int i = 0; i < count_method; i ++)
    {
       Method method = method_list[i];
       SEL sel_Method = method_getName(method);
       NSLog(@"Method class_copyMethodList:%@",NSStringFromSelector(sel_Method));
    
    }
    
    //输出
    Method class_copyMethodList:classMethod
    

    Method属性获取和操作

    首先以实例方法为例子获取一个Method
    Method ivar_Method = class_getInstanceMethod([Data class], @selector(getMethod:));

    方法调用

    /* 
     * This is faster than calling method_getImplementation() and method_getName().
     *
     * receiver 不能为空
     * 函数必须先转换成合适的函数指针,然后才能调用
     */
    void method_invoke(void /* id receiver, Method m, ... */ ) ;
    

    例如调用@selector(getMethod:)

    ((void(*)(id,Method,id))method_invoke)([[Data alloc] init], ivar_Method,@"方法调用");
    
    //输出
    getMethod:方法调用
    

    例如调用@selector(ivarMethod)

    Method ivar_Method = class_getInstanceMethod([Data class], @selector(ivarMethod));
    ((void(*)(id, Method))method_invoke)([[Data alloc] init], ivar_Method);
    
    //输出
    调用ivarMethod
    

    例如调用类方法@selector(classMethod)

    Method class_Method = class_getClassMethod([Data class], @selector(classMethod));
    //object_getClass([Data class])原理是类方法是去Metaclass寻找的
    ((void(*)(id,Method))method_invoke)(object_getClass([Data class]),class_Method);
    
    //输出
    调用classMethod
    

    关于函数指针的简单说明:
    1、void 表示返回值为空
    2、()表明是个函数指针
    3、函数指针指向函数的内存地址
    ((void()(id,Method))method_invoke)**
    4、函数指针的调用方式可以自行查阅资料

    void add(int a,int b)
    {
        int c = a+b;
        NSLog(@"%d",c);
        
    }
    
    //声明函数指针
    void (*add2)(int,int);
    //赋值
    add2 = &add;
    //调用
    (*add2)(3,4);
    ((void(*)(int, int))add)(3, 4);
        
    NSLog(@"%p--%p",&add,((void (*)(int, int))add));
    
    //输出
    7
    7
    0x107022970--0x107022970
    

    获取名称

    //获取名称
    SEL sel_ivar_Method = method_getName(ivar_Method);
    NSLog(@"Method class_getInstanceMethod:%@",NSStringFromSelector(sel_ivar_Method));
    
    //输出
    Method class_getInstanceMethod:getMethod:
    

    获取IMP

    //获取imp
    IMP method_imp = method_getImplementation(ivar_Method);
    

    获取返回值、参数类型

    const char* method_TypeEncoding = method_getTypeEncoding(ivar_Method);
    NSLog(@"method_TypeEncoding:%@",[NSString stringWithUTF8String:method_TypeEncoding]);
    
    //输出
    method_TypeEncoding:v24@0:8@16
    

    获取返回值类型

    //获取返回值类型
    const char* method_ReturnType = method_copyReturnType(ivar_Method);
    NSLog(@"method_ReturnType:%@",[NSString stringWithUTF8String:method_ReturnType]);
    //C语言获取方法 method_getReturnType(<#Method m#>, <#char *dst#>, <#size_t dst_len#>)
    
    //输出
    method_ReturnType:v
    

    获取参数个数、类型

    //获取参数个数
    unsigned int numberOfArguments = method_getNumberOfArguments(ivar_Method);
    NSLog(@"numberOfArguments:%d",numberOfArguments);
        
    //获取参数类型
    unsigned int number = method_getNumberOfArguments(ivar_Method);
    for ( int i = 0; i < number ; i ++)
    {
       const char *type = method_copyArgumentType(ivar_Method, i);
       NSLog(@"ArgumentType:%@",[NSString stringWithUTF8String:type]);
    }
     //C语言获取方法 method_getArgumentType(<#Method m#>, <#unsigned int index#>, <#char *dst#>, <#size_t dst_len#>)
    
    //输出:@表示id类型 :表示Selector
    numberOfArguments:3
    ArgumentType:@
    ArgumentType::
    ArgumentType:@
    

    获取描述

    //获取描述
    struct objc_method_description *method_descriptions = method_getDescription(ivar_Method);
    struct objc_method_description method_description = method_descriptions[0];
    SEL sel_des = method_description.name;
    const char *type_des = method_description.types;
    NSLog(@"objc_method_description:%@  types:%@",NSStringFromSelector(sel_des),[NSString stringWithUTF8String:type_des]);
    
    //输出
    objc_method_description:getMethod:  types:v24@0:8@16
    

    设置IMP

    IMP imp_new = class_getMethodImplementation([Data class], @selector(ivarMethod));
    IMP imp_old = method_setImplementation(ivar_Method, imp_new);
    ((void(*)(id,Method,id))method_invoke)([[Data alloc] init], ivar_Method,@"方法调用");
    

    //原来输出
    getMethod:方法调用
    //现在输出
    调用ivarMethod

    交换IMP

    //交换
    //用来交换的Method
    Method ivar_Method2 = class_getInstanceMethod([Data class], @selector(ivarMethod));
    
    //交换前调用
    ((void(*)(id,Method,id))method_invoke)([[Data alloc] init], ivar_Method,@"方法调用");
    ((void(*)(id,Method))method_invoke)([[Data alloc] init], ivar_Method2);
        
    //交换
    method_exchangeImplementations(ivar_Method, ivar_Method2);
        
    //交换前调用
    ((void(*)(id,Method,id))method_invoke)([[Data alloc] init], ivar_Method,@"方法调用");
    ((void(*)(id,Method))method_invoke)([[Data alloc] init], ivar_Method2);
    
    //输出结果
    getMethod:方法调用
    调用ivarMethod
    
    调用ivarMethod
    getMethod:<Data: 0x60800000d290>
    
    

    添加Method

    /** 
     * @param cls 目标类
     * @param name SEL
     * @param imp 新方法的实现函数,至少两个参数:self and _cmd.
     * @param types 参数类型列表  
     * 
     * @return 如果成功返回YES,否则NO(例如,已存在) 
     *  
     *
     * @note 可以覆盖父类方法,不改变自己已有方法(返回NO);改变已有方法:method_setImplementation。
     */
    OBJC_EXPORT BOOL class_addMethod(Class cls, SEL name, IMP imp, 
                                     const char *types) 
    

    关于types的说明请参考Type Encodings,返回值类型+self+_cmd+其他参数s:v@:其他参数符号s

    添加实例Method

    //新增方法
    -(void)ivarMethod2
    {
        NSLog(@"ivarMethod2");
    }
    
    /* 添加方法 */
    IMP imp_add = class_getMethodImplementation([self class], @selector(ivarMethod2));
    BOOL isadd =  class_addMethod([Data class], @selector(ivarMethod2), imp_add, "v@:");
    NSLog(@"isadd:%d",isadd);
        
    /* 检测添加结果 */
    unsigned int count_method = 0;
    Method *method_list = class_copyMethodList([Data class], &count_method);
    for (int i = 0; i < count_method; i ++)
    {
       Method method = method_list[i];
       SEL sel_Method = method_getName(method);
       NSLog(@"Method class_copyMethodList:%@",NSStringFromSelector(sel_Method));
    }
    
    //输出结果
    isadd:1
    Method class_copyMethodList:ivarMethod2//添加成功
    Method class_copyMethodList:getMethod:
    Method class_copyMethodList:ivarMethod
    
    

    添加类Method

    //新增方法
    +(void)classMethod2
    {
        NSLog(@"classMethod2");
    }
    
    /* 添加类方法 */
    IMP imp_add = class_getMethodImplementation([self class], @selector(classMethod2));
    BOOL isadd =  class_addMethod(object_getClass([Data class]), @selector(classMethod2), imp_add, "v@:");
    NSLog(@"isadd:%d",isadd);
        
    /* 检测添加结果 */
    unsigned int count_method = 0;
    Method *method_list = class_copyMethodList(object_getClass([Data class]), &count_method);
    for (int i = 0; i < count_method; i ++)
    {
       Method method = method_list[i];
       SEL sel_Method = method_getName(method);
       NSLog(@"Method class_copyMethodList:%@",NSStringFromSelector(sel_Method));
    }
    
    //输出结果
    isadd:1
    Method class_copyMethodList:ivarMethod2
    Method class_copyMethodList:classMethod2//添加成功
    Method class_copyMethodList:classMethod
    
    

    替换Method

    /** 
     * 
     * @param cls 目标类
     * @param name SEL
     * @param imp 新方法的实现函数
     * @param types 参数类型列表 
     * @return 旧的IMP
     * 
     * @note 两种情况:
     *  - 如果方法不存在,执行 class_addMethod  
     *    
     *  - 如果方法存在,执行method_setImplementation
     *    
     */
    OBJC_EXPORT IMP class_replaceMethod(Class cls, SEL name, IMP imp, 
                                        const char *types) 
    

    例如

    /* 替换方法 */    
    Method ivar_Method = class_getInstanceMethod([Data class], @selector(ivarMethod));
        
    //执行前
    ((void(*)(id,Method))method_invoke)([[Data alloc] init], ivar_Method);
    
    //方法替换
    IMP imp_new = class_getMethodImplementation([self class], @selector(ivarMethod2));
    IMP imp_old = class_replaceMethod([Data class], @selector(ivarMethod), imp_new, "v@:");
        
    //执行后
    ((void(*)(id,Method))method_invoke)([[Data alloc] init], ivar_Method);
    
    //输出结果
    调用ivarMethod//原来
    ivarMethod2//交换后
    

    是否响应

    /* 响应 */
    BOOL isResponse = class_respondsToSelector([Data class], @selector(ivarMethod));
    NSLog(@"isResponse:%d",isResponse);
    
    //输出结果 
    isResponse:1
    

    相关文章

      网友评论

        本文标题:Runtime--Selector、IMP、Method

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