Method 使用

runtime中Method有以下方法

具体Method的构成,包括(SEL IMP typeEncoding)请先看这里

Method在runtime中所有方法

//返回 SEL
param
OBJC_EXPORT SEL _Nonnull
method_getName(Method _Nonnull m) 
    OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);

//返回IMP
OBJC_EXPORT IMP _Nonnull
method_getImplementation(Method _Nonnull m) 
    OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);

//方法返回值和参数的类型
OBJC_EXPORT const char * _Nullable
method_getTypeEncoding(Method _Nonnull m) 
    OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);

//参数个数
OBJC_EXPORT unsigned int
method_getNumberOfArguments(Method _Nonnull m)
    OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0, 2.0);

//函数返回值描述 通过string描述
OBJC_EXPORT char * _Nonnull
method_copyReturnType(Method _Nonnull m) 
    OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);

//返回指定index的参数描述 通过string描述
OBJC_EXPORT char * _Nullable
method_copyArgumentType(Method _Nonnull m, unsigned int index) 
    OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);

//返回值类型 通过string描述
OBJC_EXPORT void
method_getReturnType(Method _Nonnull m, char * _Nonnull dst, size_t dst_len) 
    OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);

//指定index的参数类型描述 通过string描述
OBJC_EXPORT void
method_getArgumentType(Method _Nonnull m, unsigned int index, 
                       char * _Nullable dst, size_t dst_len) 
    OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);
    
//method 描述

//name: SEL 方法选择器 /  types:method_types 返回类型和参数类型
struct objc_method_description {
    SEL _Nullable name;               /**< The name of the method */  
    char * _Nullable types;           /**< The types of the method arguments */
};
OBJC_EXPORT struct objc_method_description * _Nonnull
method_getDescription(Method _Nonnull m) 
    OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);

//给Method设置IMP
OBJC_EXPORT IMP _Nonnull
method_setImplementation(Method _Nonnull m, IMP _Nonnull imp) 
    OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);

//交换两个方法
OBJC_EXPORT void
method_exchangeImplementations(Method _Nonnull m1, Method _Nonnull m2) 
    OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);

使用

定义一个方法

-(NSString *)helloWithName:(NSString *)name{
    return [NSString stringWithFormat:@"hellow %@!", name];
}
//该类下所有方法
Method method = class_getInstanceMethod(self.class, @selector(helloWithName:));
SEL sel = method_getName(method);
 const char *typeEncoding = method_getTypeEncoding(method);
unsigned int numberOfArguments = method_getNumberOfArguments(method);
const char *copyReturnType = method_copyReturnType(method);
//参数的顺序 self  _cmd 其他传过来的参数 从0计算位置
const char *copyArgumentType = method_copyArgumentType(method, 2);
char getReturnType[128];
method_getReturnType(method, getReturnType, 128);
char getArgumentType[128];
method_getArgumentType(method, 2, getArgumentType, 128);

依次对应的输出在这里插入图片描述
这里的SEL和 types的介绍在 开头的文章中有详细介绍 这里就不赘述了。
numberOfArguments 有三个是因为另外两个是self和_cmd 是隐藏参数

objc_method_description

struct objc_method_description *method_description = method_getDescription(method);
NSLog(@"\n name: %@  \n types: %@", NSStringFromSelector(method_description->name),[NSString stringWithUTF8String:method_description->types]);

objc_method_description 结构体中有两个成员分别是name和types
name 对应的是method中的SEL
types对应的是method中的method_types

method_getImplementation
获取IMP

IMP imp = method_getImplementation(method);
//声明函数
NSString* (*methodName)(id, SEL, NSString *);
//函数转换
methodName = (NSString*(*)(id, SEL, NSString *))imp;
//调用
NSString *returnString = methodName(self, @selector(helloWithName:), @"哥们");
NSLog(@"returnString: %@",returnString);

在这里插入图片描述

method_setImplementation
给方法设置IMP

//定义一个方法
static NSString* newHelloWithName(id self, SEL _cmd, NSString *name){
    return [NSString stringWithFormat:@"new hello %@!", name];
}


NSString *argument = @"哥们";
NSString *value = [self helloWithName:argument];
NSLog(@"value: %@",value);
method_setImplementation(method, (IMP)newHelloWithName);
NSString *newValue = [self helloWithName:argument];
NSLog(@"new value: %@",newValue);

在这里插入图片描述
从这里看出,可以给method重新设置一个IMP, 调用是调用新的IMP

Method_Swizzling

这里才是我们可能用到,上面其实可以忽略
关于Method_Swizzling在load中还是initialize请看这里这里

+(void)normalMethod_swizzlingWithOriginalSEL:(SEL)originalSEL swizzlingSEL:(SEL)swizzlingSEL{
    Method originalMethod = class_getInstanceMethod(self.class, originalSEL);
    Method swizzlingMethod = class_getInstanceMethod(self.class, swizzlingSEL);
    method_exchangeImplementations(originalMethod, swizzlingMethod);
}

上述方法:
在某些情况下会造成影响父类方法,比如在子类中调整从父类继承过来的方法(这里指的是子类没有重写父类的方法),会修改父类中的Method中的IMP, 父类调用该Method是在函数内部没有对应的对应的IMP而造成crash。

+(void)safeMethod_swizzlingWithOriginalSEL:(SEL)originalSEL swizzlingSEL:(SEL)swizzlingSEL{
    Method originalMethod = class_getInstanceMethod(self.class, @selector(helloWithName:));
    Method swizzlingMethod = class_getInstanceMethod(self.class, @selector(hiWithName:));
    /**
     class_addMethod 向类中添加方法,可以添加继承父类的方法(子类没有重写),但是对该类已经存在的方法会添加失败; 反之 添加失败表示class中存在SEL为originalSEL的Method
     方法添加成功表示originalSEL 对应的IMP为 method_getImplementation(swizzlingMethod)
     */
    BOOL didAddMethod = class_addMethod(self.class, originalSEL, method_getImplementation(swizzlingMethod), method_getTypeEncoding(swizzlingMethod));
    if (didAddMethod) {
        /**
            把 SEL为swizzlingSEL 对应的IMP 替换为 method_getImplementation(originalMethod)
         */
        class_replaceMethod(self.class, swizzlingSEL, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod));
    }else{
        method_exchangeImplementations(originalMethod, swizzlingMethod);
    }
}

runtime还需谨慎使用
ps: method_exchangeImplementations 其实就是 交换两个Method中的IMP

引用

ObjC 类的加载和初始化(+load 和 +initialize 方法)

猜你喜欢

转载自blog.csdn.net/wangchuanqi256/article/details/88395580