有时在代码中会有需要调用私有方法的场景,如不想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. mybatis+Oracle 批量插入数据,有数据做更新操作

    <!-- 批量添加 -->     <insert id="batchAdd" parameterType="java.util.List"& ...

  2. Google C++单元测试框架GoogleTest---AdvancedGuide(译文)

    上篇在这里: 下篇在这里.

  3. Django—views系统:views基础

    Django的View(视图)简介 一个视图函数(类),简称视图,是一个简单的Python 函数(类),它接受Web请求并且返回Web响应. 响应可以是一张网页的HTML内容,一个重定向,一个404错 ...

  4. java--springmvc

    springmvc请求图 SpringMVC内部的执行流程1.用户发起到达中央调度器DispatcherServlet2.中央调度器DispatcherServlet把请求(some.do)交给了处理 ...

  5. Django 外键、多对多插入数据方法

    models.py class UserInfo(models.Model): username = models.CharField(max_length=64,db_column='usernam ...

  6. python 套接字Socket详解

    socket简介 1. 什么是socket ? socket(简称 套接字) 是进程间通信的一种方式,它与其他进程间通信的一个主要不同是: 它能实现不同主机间的进程间通信,我们网络上各种各样的服务大多 ...

  7. CSS性能优化的8个技巧

    本文作者:高峰,360奇舞团前端工程师,W3C性能工作组成员,同时参与WOT工作组的学习. 我们都知道对于网站来说,性能至关重要,CSS作为页面渲染和内容展现的重要环节,影响着用户对整个网站的第一体验 ...

  8. 记录一下UILabel加载富文本 iOS

    一般情况下我们都习惯用webview加载富文本.但是webview比较消耗内存.然后发现其实本身可以用uilabel加载,性能还不错就记录下~~ 核心方法下面两个 -(NSMutableAttribu ...

  9. centos6.5解压及压缩zip压缩包

    查看zip压缩文件的内容而不解压:unzip -l filename.zip 将zip包解压到指定路径(若不指定路径则为当前目录):unzip filename.zip -d /usr/file 压缩 ...

  10. 1、docker简介:课程定位、是什么、能干什么、下载

    1.前提知识和定位 2.是什么 1.为什么会有docker出现 环境配置如此麻烦,换一台机器,就要重来一次,费力费时.很多人想到,能不能从根本上解决问题,软件可以带环境安装? 也就是说,安装的时候,把 ...