在上一篇咱们讲解了runtime里面关于类和分类的函数,那么,咱们这一篇就讲解下关于Method的那些函数。git
objc_method
或者Method
(这两个实际上是同一个)这个结构体在runtime.h
文件里并无详细的告诉咱们其中的成员变量,这个在这个阶段也不是很重要,后面我将会在分析runtime
源码的时候,再去分析这个结构体,如今咱们只需知道这个是关于函数的结构体。 除了objc_method
这个结构体还有一个objc_method_description
,结构以下:github
struct objc_method_description {
SEL _Nullable name; /**< The name of the method */
char * _Nullable types; /**< The types of the method arguments */
};
复制代码
name
在这里指的是函数名称,types
指的是函数的参数返回值的类型。 咱们就要经过这个method_getDescription
这个方法,能够得到objc_method_description
的结构体:api
struct objc_method_description * _Nonnull
method_getDescription(Method _Nonnull m)
OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);
复制代码
经过传入Method
返回objc_method_description
: 除了这个objc_method_description
结构体,咱们还要了解2个概念,SEL
和IMP
: SEL
:函数的选择器,通常用函数名进行绑定。 IMP
:函数的地址,用过这个能够执行函数。 关于SEL
咱们应该很熟悉,在OC中,有个方法是SEL
和NSString
互相转换,方法是SEL NSSelectorFromString(NSString *aSelectorName)
和NSString *NSStringFromSelector(SEL aSelector)
,在runtime
里面也有:bash
const char * _Nonnull
sel_getName(SEL _Nonnull sel)
OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0, 2.0);
SEL _Nonnull
sel_registerName(const char * _Nonnull str)
OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0, 2.0);
复制代码
这两个方法就能够是上面两个方法的替代方法。如今准备写个方法如何打印关于Method
的信息:函数
-(void)logMethodDescription:(Method)method {
if (method) {
struct objc_method_description * description = method_getDescription(method);
SEL selector = description->name;
char* types = description->types;
NSLog(@"selector=%s,type=%s", sel_getName(selector),types);
} else {
NSLog(@"Method 为 null");
}
}
复制代码
后面咱们去打印Method
相关的信息就能够调用logMethodDescription
方法。 另外,runtime
为了能更好的表示函数的参数和返回值的结构,特别有一张表:测试
#define _C_ID '@'
#define _C_CLASS '#'
#define _C_SEL ':'
#define _C_CHR 'c'
#define _C_UCHR 'C'
#define _C_SHT 's'
#define _C_USHT 'S'
#define _C_INT 'i'
#define _C_UINT 'I'
#define _C_LNG 'l'
#define _C_ULNG 'L'
#define _C_LNG_LNG 'q'
#define _C_ULNG_LNG 'Q'
#define _C_FLT 'f'
#define _C_DBL 'd'
#define _C_BFLD 'b'
#define _C_BOOL 'B'
#define _C_VOID 'v'
#define _C_UNDEF '?'
#define _C_PTR '^'
#define _C_CHARPTR '*'
#define _C_ATOM '%'
#define _C_ARY_B '['
#define _C_ARY_E ']'
#define _C_UNION_B '('
#define _C_UNION_E ')'
#define _C_STRUCT_B '{'
#define _C_STRUCT_E '}'
#define _C_VECTOR '!'
#define _C_CONST 'r'
复制代码
这些表明什么等咱们后面用到再说。 咱们知道方法分为实例方法和类方法,那么怎么得到他们呢,就用这两个函数:ui
Method _Nullable
class_getInstanceMethod(Class _Nullable cls, SEL _Nonnull name)
OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0, 2.0);
Method _Nullable
class_getClassMethod(Class _Nullable cls, SEL _Nonnull name)
OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0, 2.0);
复制代码
这两个函数分别表明,获取某个类的某个方法。spa
在项目里面,新增一个类Car
,定义两个实例方法-(void)function1
和-(void)function2
,两个类方法+(void)function1_class
和+(void)function2_class
。可是只实现-(void)function1
和+(void)function1_class
。咱们看下是否均可以找到。指针
@interface Car : NSObject
-(void)function1;
-(void)function2;
+(void)function1_class;
+(void)function2_class;
@end
@implementation Car
-(void)function1 {
NSLog(@"----function1----");
}
+(void)function1_class {
NSLog(@"----function1_class----");
}
@end
复制代码
而后在ViewController
里面去获取这四个方法。code
- (void)getMethod {
//获取function1
SEL selector = sel_registerName("function1");
Method method = class_getInstanceMethod(objc_getClass("Car"), selector);
[self logMethodDescription:method];
//获取function1
SEL selector1 = sel_registerName("function2");
Method method1 = class_getInstanceMethod(objc_getClass("Car"), selector1);
[self logMethodDescription:method1];
//获取function1_class
SEL selector2 = sel_registerName("function1_class");
Method method2 = class_getInstanceMethod(objc_getMetaClass("Car"), selector2);
[self logMethodDescription:method2];
//获取function2_class
SEL selector3 = sel_registerName("function2_class");
Method method3 = class_getInstanceMethod(objc_getMetaClass("Car"), selector3);
[self logMethodDescription:method3];
}
复制代码
打印结果(若是只声明方法,可是不实现Method
就会为null
):
2019-02-25 11:40:07.336465+0800 Runtime-Demo[41836:4488074] selector=function1,type=v16@0:8
2019-02-25 11:40:07.336513+0800 Runtime-Demo[41836:4488074] Method 为 null
2019-02-25 11:40:07.336523+0800 Runtime-Demo[41836:4488074] selector=function1_class,type=v16@0:8
2019-02-25 11:40:07.336539+0800 Runtime-Demo[41836:4488074] Method 为 null
复制代码
首先,咱们看到了没有实现的方法是找不到的,只有实现的方法才能找到,另外,咱们发现找类方法时候传入的cls是经过objc_getMetaClass
这个方法得到的,为何?咱们在上一篇中说过了,类方法是存在元类对象里面,而实例方法是存在类对象里面。而后咱们再看看打印出来的type
是一段奇怪的符号v16@0:8
,是否是看起来很奇怪,在这里我就直接告诉你答案:
void
(能够从上面的表格里面去找)target
,第二个selector
)SEL
类型那咱们继续,既然能够得到方法的结构体,那么也能够得到方法的地址,这可让咱们对方法进行调用:
IMP _Nullable
class_getMethodImplementation(Class _Nullable cls, SEL _Nonnull name)
OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);
IMP _Nonnull
method_getImplementation(Method _Nonnull m)
OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);
复制代码
这两个方法均可以得到IMP,只是传参不一样,第一种更直接一点。 咱们依旧以-(void)function1
和+(void)function1_class
为测试对象,看看可否进行调用
-(void)getMethodImplementation {
IMP imp1 = class_getMethodImplementation(objc_getClass("Car"), sel_registerName("function1"));
imp1();
IMP imp2 = class_getMethodImplementation(objc_getMetaClass("Car"), sel_registerName("function1_class"));
imp2();
Method method1 = class_getInstanceMethod(objc_getClass("Car"), sel_registerName("function1"));
IMP imp3 = method_getImplementation(method1);
imp3();
Method method2 = class_getInstanceMethod(objc_getMetaClass("Car"), sel_registerName("function1_class"));
IMP imp4 = method_getImplementation(method2);
imp4();
}
复制代码
打印结果:
2019-02-25 13:42:16.757607+0800 Runtime-Demo[43515:4532002] ----function1----
2019-02-25 13:42:16.757647+0800 Runtime-Demo[43515:4532002] ----function1_class----
2019-02-25 13:42:16.757659+0800 Runtime-Demo[43515:4532002] ----function1----
2019-02-25 13:42:16.757667+0800 Runtime-Demo[43515:4532002] ----function1_class----
复制代码
咱们能够看到打印的内容确实是实现的内容。咱们不只能够找到已经存在的IMP
,并且还能够为一个方法设置新的IMP
:
IMP _Nonnull
method_setImplementation(Method _Nonnull m, IMP _Nonnull imp)
OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);
复制代码
传入的是你要设置的Method
和新的IMP
,返回的是旧的IMP
; 咱们设置一个功能,让+(void)function1_class
实现-(void)function1
方法里的实现:
-(void)setImplementation {
Method method = class_getClassMethod(objc_getMetaClass("Car"), sel_registerName("function1_class"));
IMP imp = class_getMethodImplementation(objc_getClass("Car"), sel_registerName("function1"));
IMP oldIMP = method_setImplementation(method, imp);
oldIMP();
IMP newIMP = method_getImplementation(method);
newIMP();
}
复制代码
运行结果:
2019-02-25 13:55:52.261447+0800 Runtime-Demo[43745:4536866] ----function1_class----
2019-02-25 13:55:52.261486+0800 Runtime-Demo[43745:4536866] ----function1----
复制代码
咱们能够看到旧的IMP
是原来的IMP
,打印的也是原来的实现,newIMP
是设置的新的IMP
,打印的是-(void)function1
,因此没问题。
Method _Nonnull * _Nullable
class_copyMethodList(Class _Nullable cls, unsigned int * _Nullable outCount)
复制代码
这个函数是获取某个类对象的实例对象列表或者元类对象的类方法列表,outCount
是一个列表数量的指针,咱们能够经过outCount
知道列表的数量。 咱们在Car类里面再新增两个方法-(void)function3
和+(void)function3_class
,而且实现,实现内容就是打印他们的方法名。 而后,在ViewController
去使用class_copyMethodList
方法
-(void)copyMethodList {
unsigned int count1 ;
Method* instanceMethods = class_copyMethodList(objc_getClass("Car"), &count1);
unsigned int count2 ;
Method* classMethods = class_copyMethodList(objc_getMetaClass("Car"), &count2);
NSLog(@"--------------------------");
for (unsigned int i = 0; i < count1; i++) {
Method method = instanceMethods[i];
[self logMethodDescription:method];
}
NSLog(@"--------------------------");
for (unsigned int i = 0; i < count1; i++) {
Method method = classMethods[i];
[self logMethodDescription:method];
}
free(instanceMethods);
free(classMethods);
}
复制代码
打印结果:
2019-02-25 13:31:14.263003+0800 Runtime-Demo[43333:4527940] --------------------------
2019-02-25 13:31:14.263045+0800 Runtime-Demo[43333:4527940] selector=function1,type=v16@0:8
2019-02-25 13:31:14.263057+0800 Runtime-Demo[43333:4527940] selector=function3,type=v16@0:8
2019-02-25 13:31:14.263065+0800 Runtime-Demo[43333:4527940] --------------------------
2019-02-25 13:31:14.263073+0800 Runtime-Demo[43333:4527940] selector=function1_class,type=v16@0:8
2019-02-25 13:31:14.263082+0800 Runtime-Demo[43333:4527940] selector=function3_class,type=v16@0:8
复制代码
咱们看到了,仍然是只有实现的方法才能找出来 SEL
除了经过Class
和name
得到之外,还能够经过Method
来获取:
SEL _Nonnull
method_getName(Method _Nonnull m)
OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);
复制代码
例子以下:
-(void)getSEL {
Method method = class_getClassMethod(objc_getMetaClass("Car"), sel_registerName("function1_class"));
SEL sel = method_getName(method);
NSLog(@"sel = %s",sel_getName(sel));
}
复制代码
运行结果:
sel = function1_class
复制代码
能够经过method
来得到SEL
。关于SEL
还有两个方法:
BOOL
sel_isEqual(SEL _Nonnull lhs, SEL _Nonnull rhs)
OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);
BOOL
class_respondsToSelector(Class _Nullable cls, SEL _Nonnull sel)
OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);
复制代码
sel_isEqual
是用来两个SEL是否相等,class_respondsToSelector
表示某个类是否响应特定的选择器。 下面咱们来看一组api,这些都是关于参数或者返回值的函数:
//得到方法的格式
const char * _Nullable
method_getTypeEncoding(Method _Nonnull m)
OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);
//得到方法参数的个数
unsigned int
method_getNumberOfArguments(Method _Nonnull m)
OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0, 2.0);
//得到返回类型
char * _Nonnull
method_copyReturnType(Method _Nonnull m)
OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);
//得到index处的参数类型
char * _Nullable
method_copyArgumentType(Method _Nonnull m, unsigned int index)
OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);
//得到返回类型
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处的参数类型
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);
复制代码
咱们写个方法测试下这些函数:
-(void)getType {
Method method = class_getInstanceMethod(objc_getClass("Car"), sel_registerName("function1"));
const char* type = method_getTypeEncoding(method);
NSLog(@"type = %s",type);
int count = method_getNumberOfArguments(method);
NSLog(@"count = %d",count);
char* returnChar = method_copyReturnType(method);
NSLog(@"returnChar = %s",returnChar);
for (int i = 0; i < count; i++) {
char* argumentType = method_copyArgumentType(method,i);
NSLog(@"第%d个参数类型为%s",i,argumentType);
}
char dst[2] = {};
method_getReturnType(method,dst,2);
NSLog(@"returnType = %s",dst);
for (int i = 0; i < count; i++) {
char dst2[2] = {};
method_getArgumentType(method,i,dst2,2);
NSLog(@"第%d个参数类型为%s",i,dst2);
}
}
复制代码
运行结果:
2019-02-25 14:37:53.641383+0800 Runtime-Demo[44457:4553505] type = v16@0:8
2019-02-25 14:37:53.641424+0800 Runtime-Demo[44457:4553505] count = 2
2019-02-25 14:37:53.641439+0800 Runtime-Demo[44457:4553505] returnChar = v
2019-02-25 14:37:53.641459+0800 Runtime-Demo[44457:4553505] 第0个参数类型为@
2019-02-25 14:37:53.641471+0800 Runtime-Demo[44457:4553505] 第1个参数类型为:
2019-02-25 14:37:53.641495+0800 Runtime-Demo[44457:4553505] returnType = v
2019-02-25 14:37:53.641508+0800 Runtime-Demo[44457:4553505] 第0个参数类型为@
2019-02-25 14:37:53.641519+0800 Runtime-Demo[44457:4553505] 第1个参数类型为:
复制代码
返回的这些值也能够验证咱们以前说的type
的含义。
BOOL
class_addMethod(Class _Nullable cls, SEL _Nonnull name, IMP _Nonnull imp,
const char * _Nullable types)
OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);
复制代码
这个方法是给一个类增长方法。 咱们为Car类增长一个-(void)test
方法,传入的imp
则是-(void)function1
的imp
,types
就是无参无返回值。
-(void)addMethod {
IMP imp = class_getMethodImplementation(objc_getClass("Car"), sel_registerName("function1"));
BOOL addSuccess = class_addMethod(objc_getClass("Car"), sel_registerName("test"), imp, "v@:");
NSLog(@"增长不存在的方法 = %d",addSuccess);
BOOL addSuccess2 = class_addMethod(objc_getClass("Car"), sel_registerName("function1"), imp, "v@:");
NSLog(@"增长已存在的方法 = %d",addSuccess2);
}
复制代码
运行结果:
2019-02-25 14:55:29.265128+0800 Runtime-Demo[44747:4559426] 增长不存在的方法 = 1
2019-02-25 14:55:29.265327+0800 Runtime-Demo[44747:4559426] 增长已存在的方法 = 0
复制代码
这个方法是给一个类增长方法,增长成功,返回YES
,增长失败(例如,已经存在),返回NO
。
IMP _Nullable
class_replaceMethod(Class _Nullable cls, SEL _Nonnull name, IMP _Nonnull imp,
const char * _Nullable types)
OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);
复制代码
这个方法有2种状况: (一)若是SEL
存在,则至关于调用method_setImplementation
方法 (二)若是SEL
不存在,则至关于调用class_addMethod
方法
void
method_exchangeImplementations(Method _Nonnull m1, Method _Nonnull m2)
OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);
复制代码
这个函数堪称黑魔法,通常在实际用途中,是和系统方法进行交换。 咱们将function1
和function3
交换一波:
-(void)exchangeImplementations {
Method method1 = class_getInstanceMethod(objc_getClass("Car"), sel_registerName("function1"));
Method method2 = class_getInstanceMethod(objc_getClass("Car"), sel_registerName("function3"));
//交换方法
method_exchangeImplementations(method1, method2);
//此时的function1的实现应该是`function3`的实现
IMP imp1 = method_getImplementation(method1);
//此时的function3的实现应该是`function1`的实现
IMP imp2 = method_getImplementation(method2);
imp1();
imp2();
}
复制代码
运行结果:
2019-02-25 15:16:19.507945+0800 Runtime-Demo[45112:4568994] ----function3----
2019-02-25 15:16:19.507982+0800 Runtime-Demo[45112:4568994] ----function1----
复制代码
确实交换过来了。第二篇就这么结束了,第三篇我将讲解关于属性和变量的函数。 对了,这个是demo,喜欢的能够点个星。