有时在代码中会有需要调用私有方法的场景,如不想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调用私有方法的更多相关文章

  1. 反射工具类.提供调用getter/setter方法, 访问私有变量, 调用私有方法, 获取泛型类型Class,被AOP过的真实类等工具函数.java

    import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.Validate; import org.ap ...

  2. java反射调用私有方法和修改私有属性

    //调用私有方法package com.java.test; public class PrivateMethod { private String sayHello(String name) { r ...

  3. php通过反射方法调用私有方法

    PHP 5 具有完整的反射 API,添加了对类.接口.函数.方法和扩展进行反向工程的能力. 下面我们演示一下如何通过反射,来调用执行一个类中的私有方法: <?php //MyClass这个类中包 ...

  4. 利用JAVA反射机制实现调用私有方法

    1.fragment是AccessibilityFragment的對象.须要被調用的方法的類. setAccessible(true)并非将方法的訪问权限改成了public.而是取消java的权限控制 ...

  5. ios runtime部分事例方法说明

    一.场景--动态改变变量 unsigned ; Ivar *ivar = class_copyIvarList([self.person class], &count); ; i<cou ...

  6. Java反射机制调用私有方法

    1.获取目标类: 每个类都有一个class属性,通过实体类的class属性获取: Class clazz = Person.class 通过对象获取.  Person p1 = new Person( ...

  7. 从vs2010的UnitTestFramework类库提取私有方法反射调用的方法

    背景 年龄大点的程序员都知道在vs2010中创建单元测试非常的简单,鼠标定位在方法名字,右键创建单元测试,就会创建一个测试方法,即使是在私有方法上也可以创建测试方法. VS2010以后就没这么简单了, ...

  8. 【转】【Java】利用反射技术,实现对类的私有方法、变量访问

    java关于反射机制的包主要在java.lang.reflect中,structs,hibernate,spring等框架都是基于java的反射机制. 下面是一个关于利用java的反射机制,实现了对私 ...

  9. Javascript 面向对象(共有方法,私有方法,特权方法,静态属性和方法,静态类)示例讲解

    一,私有属性和方法 私有方法:私有方法本身是可以访问类内部的所有属性(即私有属性和公有属性),但是私有方法是不可以在类的外部被调用. <script> /* * 私有方法:私有方法本身是可 ...

随机推荐

  1. tomcat 部署 React 项目后,浏览器刷新报404问题

    问题:tomcat部署了react前端项目,可以正常访问,但是页面刷新就报404 一.问题截图 二.解决办法 在tomcat 配置文件web.xml中配置如下代码: web.xml 路径: apach ...

  2. Spring Cloud(五)断路器监控(Hystrix Dashboard)

    在上两篇文章中讲了,服务提供者 Eureka + 服务消费者 Feign,服务提供者 Eureka + 服务消费者(rest + Ribbon),本篇文章结合,上两篇文章中代码进行修改加入 断路器监控 ...

  3. 轻量化模型之MobileNet系列

    自 2012 年 AlexNet 以来,卷积神经网络在图像分类.目标检测.语义分割等领域获得广泛应用.随着性能要求越来越高,AlexNet 已经无法满足大家的需求,于是乎各路大牛纷纷提出性能更优越的 ...

  4. namespace" 或The content of element type "mapper" must match "EMPTY"

    必须为元素类型 "mapper" 声明属性 "namespace" 或The content of element type "mapper" ...

  5. 并发编程: 生产消费模型、死锁与Rlock、线程、守护线程、信号量、锁

    一.守护进程 二.互斥锁 三.抢票 四.进程间通讯 五.进程间通讯2 一.守护进程 """ 进程间通讯的另一种方式 使用queue queue 队列 队列的特点: 先进的 ...

  6. 单例模式(Singleton)的同步锁synchronized

    单例模式,有“懒汉式”和“饿汉式”两种. 懒汉式 单例类的实例在第一次被引用时候才被初始化. public class Singleton { private static Singleton ins ...

  7. python打开excel跳转对应分页

    需求: 在python脚本层,直接打开excel,跳转特定分页,方便策划编辑 尝试了几种不同的方法,最终选择了方法4.4种方法都可以实现打开外部文件,3.4可以实现跳转特定分页.3和4的区别是,3通过 ...

  8. mybatis注解大全

    注解 目标 相对应的 XML 描述 @CacheNamespace 类 <cache> 为给定的命名空间 (比如类) 配置缓存. 属性:implemetation,eviction, fl ...

  9. RabbitMQ概要

    安装: 1.Erlang安装 安装RabbitMQ需要先安装Erlang,这里选择otp21.2-win-64下载完成后安装 资源: http://www.erlang.org/downloads 2 ...

  10. 题解 [BZOJ4368][IOI2015]boxes纪念品盒

    题面 解析 可以发现,发纪念品有三种方式: 从左边走再原路返回. 从右边走再原路返回. 走一圈. 注意到,第三种走法最多只会走一次, 因为如果走了多次,那发放的物品数量就会>=\(2k\), 那 ...