ios-Runtime调用私有方法
有时在代码中会有需要调用私有方法的场景,如不想import太多头文件;想组件设计一些解耦的模块;查看别人模块中未暴露的代码进行分析等。
在 ios 中调用私有方法有很多种方式,主要是通过Runtime去实现。下面自己也测试一下。
新建一个Person类,Person.h中不写代码,Person.m中如下:
#import "Person.h" @implementation Person - (void)eat
{
NSLog(@"xxx eat====");
} - (void)eat:(NSString *)str str2:(NSString *)str2 str3:(NSString *)str3
{
NSLog(@"xxx eat====%@==%@==%@", str, str2, str3);
} @end
【找到该类methodLists里的方法】
要想调用私有方法,首先要知道类有什么哪些方法。可以通过如下代码得到方法的一些信息:(不管私有还是公有,只要在该类的methodLists里)
// 获取实例方法
- (void)getMethods
{
int outCount = ;
Person *p = [Person new];
Method *methods = class_copyMethodList([p class], &outCount);
for (int i = ; i < outCount; i ++) {
NSLog(@"=============%d", i);
// 获取方法名
Method method = methods[i];
SEL methodName = method_getName(method);
NSLog(@"方法名= %@", NSStringFromSelector(methodName)); // 获取参数
char argInfo[] = {};
unsigned int argCount = method_getNumberOfArguments(method);
for (int j = ; j < argCount; j ++) {
// 参数类型
method_getArgumentType(method, j, argInfo, );
NSLog(@"参数类型= %s", argInfo);
memset(argInfo, '\0', strlen(argInfo));
} // 获取方法返回值类型
char retType[] = {};
method_getReturnType(method, retType, );
NSLog(@"返回类型值类型= %s", retType);
}
free(methods);
}
上面代码使用runtime获取一些方法的信息:方法名,参数对应的类型,返回值类型。上面这个方法打印结果如下:
=============
方法名= eat
参数类型= @
参数类型= :
返回类型值类型= v
=============
方法名= eat:str2:str3:
参数类型= @
参数类型= :
参数类型= @
参数类型= @
参数类型= @
返回类型值类型= v
打印的类型是一些符号,不知道这是什么鬼,但其实这是苹果的类型编码。它对照的含义如下:
v A void —— (为空)
@ An object (whether statically typed or typed id) —— (id类型)
: A method selector (SEL) —— (方法名)
上面打印的方法信息中 eat 方法也有两个参数,实际每个方法都有两个隐藏参数。(_cmd是当前方法编号)
【调用私有方法】
调用私有方法有多种方式,但其实最终都大同小异。如下:
1. 使用 performSelector 下面2和3行结果一样。这样比使用对象直接调用好处是编译器不会报错,也不用方法暴露头文件。
Person *p = [Person new];
[p performSelector:@selector(eat)]; // log: xxx eat====
[p performSelector:NSSelectorFromString(@"eat")]; // log: xxx eat====
2. 使用 objc_msgSend ,对比上面 objc_msgSend 好处是传递多个参数时更为方便。objc_msgSend深入学习
Person *p = [Person new]; // 需要引用 Person.h 头文件
objc_msgSend(p, @selector(eat:str2:str3:), @"", @"", @""); // log: eat====1==2==3
3. 利用函数实现IMP,IMP类型结构与objc_msgSend底层是同一类型,与2中实现无差别
Person *p = [Person new];
IMP imp = [p methodForSelector:@selector(eat)];
void (* tempFunc)(id target, SEL) = (void *)imp;
tempFunc(p, @selector(eat)); // log: xxx eat====
4. 使用类对象发送消息,上面3种方式都需要引用 Person.h 头文件,这里类对象进行调用可以解决这个问题。
Class pClassObj = NSClassFromString(@"Person");
objc_msgSend([pClassObj new], @selector(eat));
【类对象】
进入 objc.h 中查看 Class 与 Object 结构定义。每个 objc_object 下都有一个 isa 指针指向这个对象所属的 objc_class。
/// An opaque type that represents an Objective-C class.
typedef struct objc_class *Class; /// Represents an instance of a class.
struct objc_object {
Class _Nonnull isa OBJC_ISA_AVAILABILITY;
};
再看一下 objc_class 定义的结构。它里面只有一个 isa 指针,下面那些属性在 objc2 中已经不可用了。
struct objc_class {
Class _Nonnull isa OBJC_ISA_AVAILABILITY;
#if !__OBJC2__
Class _Nullable super_class OBJC2_UNAVAILABLE;
const char * _Nonnull name OBJC2_UNAVAILABLE;
long version OBJC2_UNAVAILABLE;
long info OBJC2_UNAVAILABLE;
long instance_size OBJC2_UNAVAILABLE;
struct objc_ivar_list * _Nullable ivars OBJC2_UNAVAILABLE;
struct objc_method_list * _Nullable * _Nullable methodLists OBJC2_UNAVAILABLE;
struct objc_cache * _Nonnull cache OBJC2_UNAVAILABLE;
struct objc_protocol_list * _Nullable protocols OBJC2_UNAVAILABLE;
#endif
} OBJC2_UNAVAILABLE;
它里面的 isa 指向的是 MetaClass (元类)。Class 和 MetaClass :
- 当我们对一个实例发送消息时(-开头的方法),会在该 instance 对应的类的 methodLists 里查找。
- 当我们对一个类发送消息时(+开头的方法),会在该类的 MetaClass 的 methodLists 里查找。

- 每个 Class 都有一个 isa 指针指向一个唯一的 Meta Class
- 每一个 Meta Class 的 isa 指针都指向最上层的 Meta Class,即 NSObject 的 MetaClass,而最上层的 MetaClass 的 isa 指针又指向自己
获取类对象
[NSObject class];
获取元类对象
Class class = [NSObject class];
Class metaClass = objc_getClass(class);
【实例对象成员变量】
上面可以知道,实例对象的方法存放在它对应类对象 (class) 的 methodsList 中,类方法存放它对应的元类 (metaClass) 的 methodsList 中。
一个 new 出来的对象除了它的方法,它还有成员变量。
- 成员变量是存放在实例对象之中
假如有一个 Person 对象,这个实例对象结构体中存放信息应该是这样,即成员变量存放在结构体,方法信息通过 isa 去找相应的类对象。
struct Student_IMPL {
Class isa;
int _age;
int _height;
};
ios-Runtime调用私有方法的更多相关文章
- 反射工具类.提供调用getter/setter方法, 访问私有变量, 调用私有方法, 获取泛型类型Class,被AOP过的真实类等工具函数.java
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.Validate; import org.ap ...
- java反射调用私有方法和修改私有属性
//调用私有方法package com.java.test; public class PrivateMethod { private String sayHello(String name) { r ...
- php通过反射方法调用私有方法
PHP 5 具有完整的反射 API,添加了对类.接口.函数.方法和扩展进行反向工程的能力. 下面我们演示一下如何通过反射,来调用执行一个类中的私有方法: <?php //MyClass这个类中包 ...
- 利用JAVA反射机制实现调用私有方法
1.fragment是AccessibilityFragment的對象.须要被調用的方法的類. setAccessible(true)并非将方法的訪问权限改成了public.而是取消java的权限控制 ...
- ios runtime部分事例方法说明
一.场景--动态改变变量 unsigned ; Ivar *ivar = class_copyIvarList([self.person class], &count); ; i<cou ...
- Java反射机制调用私有方法
1.获取目标类: 每个类都有一个class属性,通过实体类的class属性获取: Class clazz = Person.class 通过对象获取. Person p1 = new Person( ...
- 从vs2010的UnitTestFramework类库提取私有方法反射调用的方法
背景 年龄大点的程序员都知道在vs2010中创建单元测试非常的简单,鼠标定位在方法名字,右键创建单元测试,就会创建一个测试方法,即使是在私有方法上也可以创建测试方法. VS2010以后就没这么简单了, ...
- 【转】【Java】利用反射技术,实现对类的私有方法、变量访问
java关于反射机制的包主要在java.lang.reflect中,structs,hibernate,spring等框架都是基于java的反射机制. 下面是一个关于利用java的反射机制,实现了对私 ...
- Javascript 面向对象(共有方法,私有方法,特权方法,静态属性和方法,静态类)示例讲解
一,私有属性和方法 私有方法:私有方法本身是可以访问类内部的所有属性(即私有属性和公有属性),但是私有方法是不可以在类的外部被调用. <script> /* * 私有方法:私有方法本身是可 ...
随机推荐
- OpenCl入门——实现简单卷积
现在的卷积实现无非是那么几种:直接卷积.im2col+gemm.局部gemm.wingrod.FFT.如果直接卷积的话,其实kernel函数是比较好实现.以下代码参考至<OpenCL Progr ...
- BRD——>MRD——>PRD,产品经理三大文档概念详解及前后逻辑
转自:https://blog.csdn.net/neikutaixiao/article/details/40819445 商业需求文档Business Requirement DocumentBR ...
- iPhone 11来了
- C#面向对象(抽象类、接口、构造函数、重载、静态方法和静态成员)
1.抽象类 抽象类关键词 abstract (抽象) override (重写) 在父集中用 abstract 表示抽象类,抽象方法,在子集中用 override 改写 抽 ...
- 针对nginx应用场景的配置 知识整理
本文为转载,原文链接 前言 原本想写整理一篇针对nginx应用场景的相应配置,但发现已经有人整理了,而且写得非常不错,特意转过来 概论 Nginx 是一款面向性能设计的 HTTP 服务器,能反向代理 ...
- jumpserver跳板机docker安装小小趟坑
最近日常运维的时候发现每次登陆服务器都要打开终端目录连接对应的服务器,闲暇的时候还好,运维任务很重的时候才发现这样的玩法很傻,浪费时间且一点儿都跟不上潮流,然后打开githup开始搞起来.docker ...
- idea配置代码注释模板
从eclipse换成idea后,有点不习惯,其中之一就是代码注释,感觉不如eclipse好用,下面是一些配置方法,配完之后差不多能实现eclipse的效果. 1.以配置Class的注释为例,其他文件的 ...
- 记一次CDN大量探测处理方法,UA限制返回403
起因: 发现源站流量阶段性异常,基本上每2小时高发到50M左右,并持续30分钟左右 排除过程: 在流量正常的时候,排查了各种可能性,均未果,观察监控,在流量再次增大时,发现该服务器上某一域名的访问日志 ...
- 封装Server类和Client类
服务器端: EasyTcpServer.hpp #ifndef _EasyTcpServer_hpp_ #define _EasyTcpServer_hpp_ #ifdef _WIN32 #defin ...
- List.ForEach批量新增并发异常解决
批量新增操作在业务系统中十分常见,尤其是主从表中对从表的批量处理.昨天在对wms主从表进行业务操作时使用了c#中list自带的函数ForEach对从表批量新增,代码如下: 在无并发的情况下接口请求正常 ...