runtime Method

原文出自:标哥的技术博客

前言

本篇文章只讲Method的特性及相关方法,不讲Method Swizzling特性。关于Method Swizzling特性,我们放在单独的一篇文章来细讲,因为这一节非常重要。

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的方法,我们一一说明其用途,看代码注释:

// 函数调用,但是不接收返回值类型为结构体
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

获取函数列表

我们尝试获取函数列表,并细说函数的参数type encoding、返回值类型等。我们先写以下几个方法:

- (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_getName()函数来获取方法SEL

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

为什么多了两个参数

通过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 代表long类型,在64位处理器上也是按照32位处理
q 代表long long类型
C 代表unsigned char类型
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)

关注我

如果在使用过程中遇到问题,或者想要与我交流,可加入有问必答QQ群:324400294

关注微信公众号:iOSDevShares

关注新浪微博账号:标哥Jacky

标哥的GITHUB地址:CoderJackyHuang

支持并捐助

如果您觉得文章对您很有帮忙,希望得到您的支持。您的捐肋将会给予我最大的鼓励,感谢您的支持!

支付宝捐助 微信捐助
时间: 2024-11-02 12:08:21

runtime Method的相关文章

Runtime Method Swizzling

原文出自:标哥的技术博客 前言 在我学习runtime的method swizzling特性之前,有很多同事或者朋友经常在我耳边说起swizzling特性,一个个在我面前说这个东西千万不能用,会引起很多问题的.但是,在我学习完这一节的知识后,我终于明白其所以然. 学习完swizzling特性后,我很喜欢她.她就像一把双刃剑,用好了可以带你飞,乱用则会反伤.但是,我更相信她的强大,更相信自己够能驾驭她!一起来学习吧! Method Swizzling 试想一下,苹果的源码是闭源的,我们只有类名和类

Java JDK1.5、1.6、1.7新特性整理_java

一.Java JDK1.5的新特性 1.泛型: List<String> strs = new ArrayList<String>();//给集合指定存入类型,上面这个集合在存入数据的时候必须存入String类型的数据,否则编译器会报错 2.for-each 例如上面这个集合我们可以通过for-each遍历,这样更加简单清晰 for(String s : strs){ System.out.println(s); } 注意:使用for-each遍历集合时,要遍历的集合必须实现了It

通过runtime获取对象相关信息

通过runtime获取对象相关信息 在这里,本人给大家提供一个runtime关于NSObject的扩展,用来显示各种NSObject中的信息,这有助于你来分析类的组成:) 先准备以下类供测试: Model.h 与 Model.m // // Model.h // Runtime // // Copyright (c) 2014年 Y.X. All rights reserved. // #import <Foundation/Foundation.h> typedef enum : NSUIn

Overloading overriding runtime type and object orientation (1)

loading|object 6)Overloading overriding runtime type and object orientationObjective 1)State the benefits of encapsulation in object oriented design and write code that implements tightly encapsulated classes and the relationships "is a" and &qu

Raise Method

Syntaxobject.Raise(number, source, description, helpfile, helpcontext)The Raise method has these parts: Part Description object: Always the Err object. number: A Long integer subtype that identifies the nature of the error. VBScript errors (both VBSc

Weex 在 JS Runtime 内的多实例管理

Weex 的技术架构和传统的客户端渲染机制相比有一个显著的差别,就是引入了 JavaScript,通过 JS Runtime 完成一些动态性的运算,再把运算结果和外界进行通信,完成界面渲染等相关操作指令.而客户端面对多个甚至可能同时共存的 Weex 页面时,并没有为每个 Weex 页面提供各自独立的 JS Runtime,相反我们只有一个 JS Runtime,这意味着所有的 Weex 页面共享同一份 JS Runtime,共用全局环境.变量.内存.和外界通信的接口等等.这篇文章会循序渐进的介绍

Runtime那些事儿(消息机制)

本文是投稿文章,作者:HenryCheng 一.关于runtime 之前在项目中有遇到过用runtime解决改变全局字体的问题,所以再一次感受到了runtime黑魔法的强大,趁现在有机会分享一下对runtime的一些理解.在对象调用方法是Objective-C中经常使用的功能,也就是消息的传递,而Objective-C是C的超集,所以和C不同的是,Objective-C使用的是动态绑定,也就是runtime.Objective-C的消息传递和消息机制也就不多说了,今天主要说的是动态方法,也就是函

从AOP框架学习iOS Runtime

最近在一个iOS项目中添加了一个AOP框架,并且根据项目需求做了一些重构,重构的过程中对AOP的实现方式也做了下学习和分析,感觉还是很有趣的,下面就给大家分享一下个人所得.     在正式讲解iOS中AOP实现原理之前,有些准备知识需要讲一下      准备知识          准备知识一:Method,SEL,IMP概念     SEL      先看一下SEL的概念,Objective-C在编译时,会依据每一个方法的名字.参数序列,生成一个唯一的整型标识(Int类型的地址),这个标识就是S

iOS运行时(Runtime)总结

声明 本博客中文章不会在此处再更新,只会在微信公众号中更新,请关注微信公众号,以获取最新的学习资源和更多学习资源.本博文末尾有微信公众号二维码,扫一扫添加关注. 原文出自:微信公众号iOSDevShares的文章 引言 相信很多同学都听过运行时,但是我相信还是有很多同学不了解什么是运行时,到底在项目开发中怎么用?什么时候适合使用?想想我们的项目中,到底在哪里使用过运行时呢?还能想起来吗?另外,在面试的时候,是否经常有笔试中要求运用运行时或者在面试时面试官会问是否使用过运行时,又是如何使用的? 回