美文网首页
runtime 基础篇(二)

runtime 基础篇(二)

作者: 东方_未明 | 来源:发表于2016-06-14 15:57 被阅读362次

转载标哥的一篇博客, 写的真是详细

Method类型

Method类型是一个objc_method结构体指针,而结构体objc_method有三个成员:

/// An opaque type that represents a method in a class definition.
typedef struct objc_method *Method;

struct objc_method {
    SEL method_name;        // 方法名称
    char *method_typesE;    // 参数和返回类型的描述字串
    IMP method_imp;         // 方法的具体的实现的指针
} 

Method所有方法

// 函数调用,但是不接收返回值类型为结构体
method_invoke
// 函数调用,但是接收返回值类型为结构体
method_invoke_stret
// 获取函数名
method_getName
// 获取函数实现IMP
method_getImplementation
// 获取函数type encoding
method_getTypeEncoding
// 复制返回值类型
method_copyReturnType
// 复制参数类型
method_copyArgumentType
// 获取返回值类型
method_getReturnType
// 获取参数个数
method_getNumberOfArguments
// 获取函数参数类型
method_getArgumentType
// 获取函数描述
method_getDescription
// 设置函数实现IMP
method_setImplementation
// 交换函数的实现IMP
method_exchangeImplementations

获取函数列表

- (int)testInstanceMethod:(NSString *)name andValue:(NSNumber *)value {
  NSLog(@"%@", name);

  return value.intValue;
}

- (NSArray *)arrayWithNames:(NSArray *)names {
  NSLog(@"%@", names);
  return names;
}

- (void)getMethods {
  unsigned int outCount = 0;
  Method *methodList = class_copyMethodList(self.class, &outCount);

  for (unsigned int i = 0; i < outCount; ++i) {
    Method method = methodList[i];

    SEL methodName = method_getName(method);
    NSLog(@"方法名:%@", NSStringFromSelector(methodName));

    // 获取方法的参数类型
    unsigned int argumentsCount = method_getNumberOfArguments(method);
    char argName[512] = {};
    for (unsigned int j = 0; j < argumentsCount; ++j) {
      method_getArgumentType(method, j, argName, 512);

      NSLog(@"第%u个参数类型为:%s", j, argName);
      memset(argName, '\0', strlen(argName));
    }

    char returnType[512] = {};
    method_getReturnType(method, returnType, 512);
    NSLog(@"返回值类型:%s", returnType);

    // type encoding
    NSLog(@"TypeEncoding: %s", method_getTypeEncoding(method));
  }

  free(methodList);
}

我们写一个测试方法来调用一下:

+ (void)test {
  HYBMethodLearn *m = [[HYBMethodLearn alloc] init];
  [m getMethods];

  // 这就是为什么有四个参数的原因
  int returnValue = ((int (*)(id, SEL, NSString *, NSNumber *))objc_msgSend)((id)m, @selector(testInstanceMethod:andValue:), @"标哥的技术博客", @100);
  NSLog(@"return value is %d", returnValue);
}

其打印结果如下:

方法名:getMethods
第0个参数类型为:@
第1个参数类型为::
返回值类型:v
TypeEncoding: v16@0:8
方法名:testInstanceMethod:andValue:
第0个参数类型为:@
第1个参数类型为::
第2个参数类型为:@
第3个参数类型为:@
返回值类型:i
TypeEncoding: i32@0:8@16@24
方法名:arrayWithNames:
第0个参数类型为:@
第1个参数类型为::
第2个参数类型为:@
返回值类型:@
TypeEncoding: @24@0:8@16
标哥的技术博客
return value is 100

为什么多了两个参数

通过method_getNumberOfArguments获取到函数的所有参数类型,从上面我们所定义的函数中,比如getMethods明明没有参数,为什么会打印出来两个参数呢?而- (int)testInstanceMethod:(NSString *)name andValue:(NSNumber *)value明明只有两个参数,为什么有四个参数呢?下面我们来细说:

  • 首先,对于第一个方法,它在编译时会被转换成类似这样:
((void (*)(id, SEL))objc_msgSend)((id)m, @selector(getMethods));
  • 同样的道理,对于第二个方法,在编译时编译器会将其转换成类似这样:
// 这就是为什么有四个参数的原因
int returnValue = ((int (*)(id, SEL, NSString *, NSNumber *))
                 objc_msgSend)((id)m,
                               @selector(testInstanceMethod:andValue:),
                               @"标哥的技术博客",
                               @100);

函数编码

通过method_getTypeEncoding获取函数的编码,其结果是一串值。

  • 第一个方法的编码为:v16@0:8
  • 第二个方法的编码为:i32@0:8@16@24
  • 第三个方法的编码为:@24@0:8@16

这么一看,可以看出来什么呢?从这几个值可以看出:

  • 第一个位置是返回值类型,比如第一个方法返回值是V,第二个的是i,第三个的是@
  • 第二/三个位置是第一/二个参数,参数列表从左到右算。分别是@ :,@ :,@ :,都是对象,其实第一个和第二个参数是固定的,第一个是接收消息的对象,而第二个是方法选择器SEL。
  • 如果还有其它参数,依次…

但是类型后面跟着的数字是什么呢?其实笔者也不清楚,文档没有明确说明,不过从其打印结果可以看得出来其规律。比如第一个方法的:@的偏移为0、:的偏移为8、v的偏移为16,其它方法也是类似。

method_invoke

除了使用objc_msgSend函数之外,还可以使用method_invoke,如:

// 获取方法
Method method = class_getInstanceMethod([self class], @selector(testInstanceMethod:andValue:));

// 调用函数
returnValue = ((int (*)(id, Method, NSString *, NSNumber *))method_invoke)((id)m, method, @"测试使用method_invoke", @11);
NSLog(@"call return vlaue is %d", returnValue);

// 与下面的调用可以得到同样的效果
int returnValue = ((int (*)(id, SEL, NSString *, NSNumber *))
                 objc_msgSend)((id)m,
                               @selector(testInstanceMethod:andValue:),
                               @"标哥的技术博客",
                               @100);
NSLog(@"return value is %d", returnValue);

Type Encoding

编码值 含意
c 代表char类型
i 代表int类型
s 代表short类型
l(小写L) 代表long类型,在64位处理器上也是按照32位处理
q 代表long long类型
C 代表unsigned char类型
I(大写i) 代表unsigned int类型
S 代表unsigned short类型
L 代表unsigned long类型
Q 代表unsigned long long类型
f 代表float类型
d 代表double类型
B 代表C++中的bool或者C99中的_Bool
v 代表void类型
* 代表char *类型
@ 代表对象类型
# 代表类对象 (Class)
: 代表方法selector (SEL)
[array type] 代表array
{name=type…} 代表结构体
(name=type…) 代表union
bnum A bit field of num bits
^type A pointer to type
? An unknown type (among other things, this code is used for function pointers)

相关文章

  • iOS之Runtime(一)

    一:@@@《基础篇》@@@ 二:@@@《应用篇》@@@ 目录: 一、理解OC是动态语言,Runtime又是什么?二...

  • runtime 基础篇(二)

    转载标哥的一篇博客, 写的真是详细 Method类型 Method类型是一个objc_method结构体指针,而结...

  • iOS runtime(实践篇)

    iOS runtime(基础篇)iOS runtime(理论篇)前面2篇介绍了runtime一些概念。接下来看看r...

  • iOS运行时Runtime

    相关文档:Runtime-iOS运行时基础篇 相关文档:Runtime-iOS运行时应用篇

  • YYModel源码详细解析-1

    前言: 阅读YYModel之前建议先阅读Runtime基础篇,YYModel采用Runtime直接调用 Gette...

  • ios runtime 详解

    前言 在开始之前建议先阅读iOS runtime的基础理解篇:iOS内功篇:runtime 有筒子在面试的时候,遇...

  • iOS runtime实战应用:成员变量和属性

    前言 在开始之前建议先阅读iOS runtime的基础理解篇:iOS内功篇:runtime 有筒子在面试的时候,遇...

  • iOS runtime实战应用:关联对象

    前言 在开始之前建议先阅读iOS runtime的基础理解篇:iOS内功篇:runtime 有筒子在面试的时候,遇...

  • Runtime - 基础篇

    NSObject方法 Cocoa中大多数对象是继承自NSObjectl类的子类(唯一例外的是NSProxy类)NS...

  • Runtime基础篇

    转载:https://www.jianshu.com/p/633e5d8386a8 本文用来介绍 iOS 开发中『...

网友评论

      本文标题:runtime 基础篇(二)

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