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 方法)
本文链接:https://my.lmcjl.com/post/8939.html
展开阅读全文
4 评论