Runtime初识
什么是Runtime
OC中一切都被设计成了对象,我们都知道一个类被初始化成一个实例,这个实例是一个对象。实际上一个类本质上也是一个对象,在runtime中用结构体表示。
相关的定义
/// 描述类中的一个方法
typedef struct objc_method *Method;
/// 实例变量
typedef struct objc_ivar *Ivar;
/// 类别Category
typedef struct objc_category *Category;
/// 类中声明的属性
typedef struct objc_property *objc_property_t;
类在runtime中的表示
struct objc_class {
Class isa;//指针,顾名思义,表示是一个什么,
//实例的isa指向类对象,类对象的isa指向元类
#if !__OBJC2__
Class super_class; //指向父类
const char *name; //类名
long version;
long info;
long instance_size
struct objc_ivar_list *ivars //成员变量列表
struct objc_method_list **methodLists; //方法列表
struct objc_cache *cache;//缓存
//一种优化,调用过的方法存入缓存列表,下次调用先找缓存
struct objc_protocol_list *protocols //协议列表
#endif
} OBJC2_UNAVAILABLE;
/* Use `Class` instead of `struct objc_class *` */
获取列表
unsigned int count;
//获取属性列表
objc_property_t *propertyList = class_copyPropertyList([self class], &count);
for (unsigned int i=; i<count; i++) {
const char *propertyname =property_getName(propertyList[i]);
NSLog(@"property----="">%@", [NSString stringWithUTF8String:propertyname]);
}
//获取方法列表
Method *methodList = class_copyMethodList([self class], &count);
for (unsigned int i; i<count; i++) {
Method method = methodList[i];
NSLog(@"method----="">%@", NSStringFromSelector(method_getName(method)));
}
//获取成员变量列表
Ivar *ivarList = class_copyIvarList([self class], &count);
for (unsigned int i; i<count; i++) {
Ivar myivar =ivarList[i];
const char *ivarname =ivar_getName(myivar);
NSLog(@"ivar----="">%@", [NSString stringWithUTF8String:ivarname]);
}
//获取协议列表
__unsafe_unretained Protocol **protocolList = class_copyProtocolList([self class], &count);
for (unsigned int i; i<count; i++) {
Protocol *myprotocal = protocolList[i];
const char *protocolname = protocol_getName(myprotocal);
NSLog(@"protocol----="">%@", [NSString stringWithUTF8String:protocolname]);
}
方法调用
如果用实例对象调用实例方法,会到实例的isa指针指向的对象(也就是类对象)操作。
如果调用的是类方法,就会到类对象的isa指针指向的对象(也就是元类对象)中操作。
首先,在相应操作的对象中的缓存方法列表中找调用的方法,如果找到,转向相应实现并执行。
如果没找到,在相应操作的对象中的方法列表中找调用的方法,如果找到,转向相应实现执行
如果没找到,去父类指针所指向的对象中执行,2.
以此类推,如果一直到根类还没找到,转向拦截调用。
如果没有重写拦截调用的方法,程序报错。
以上的过程给我带来的启发:
重写父类的方法,并没有覆盖掉父类的方法,只是在当前类对象中找到了这个方法后就不会再去父类中找了。
如果想调用已经重写过的方法的父类的实现,只需使用super这个编译器标识,它会在运行时跳过在当前的类对象中寻找方法的过程。
拦截调用
那么什么是拦截调用呢。
拦截调用就是,在找不到调用的方法程序崩溃之前,你有机会通过重写NSObject的四个方法来处理。
+ (BOOL)resolveClassMethod:(SEL)sel;
+ (BOOL)resolveInstanceMethod:(SEL)sel;
//后两个方法需要转发到其他的类处理
- (id)forwardingTargetForSelector:(SEL)aSelector;
- (void)forwardInvocation:(NSInvocation *)anInvocation;
第四个方法是将你调用的不存在的方法打包成NSInvocation传给你。做完你自己的处理后,调用invokeWithTarget:方法让某个target触发这个方法。
动态添加方法
有一个办法是根据传进来的SEL类型的selector动态添加一个方法。
首先从外部隐式调用一个不存在的方法:
//隐式调用方法
[target performSelector:@selector(resolveAdd:) withObject:@"test"]; 然后,在target对象内部重写拦截调用的方法,动态添加方法。 void runAddMethod(id self, SEL _cmd, NSString *string){
NSLog(@"add C IMP ", string);
}
+ (BOOL)resolveInstanceMethod:(SEL)sel{
//给本类动态添加一个方法
if ([NSStringFromSelector(sel) isEqualToString:@"resolveAdd:"]) {
class_addMethod(self, sel, (IMP)runAddMethod, "v@:*");
}
return YES;
}
其中class_addMethod的四个参数分别是:
Class cls 给哪个类添加方法,本例中是self
SEL name 添加的方法,本例中是重写的拦截调用传进来的selector。
IMP imp 方法的实现,C方法的方法实现可以直接获得。如果是OC方法,可以用
"v@:*"方法的签名,代表有一个参数的方法。
关联对象
现在你准备用一个系统的类,但是系统的类并不能满足你的需求,你需要额外添加一个属性。这种情况的一般解决办法就是继承。但是,只增加一个属性,就去继承一个类,总是觉得太麻烦类。这个时候,runtime的关联属性就发挥它的作用了。
//首先定义一个全局变量,用它的地址作为关联对象的key
static char associatedObjectKey;
//设置关联对象
objc_setAssociatedObject(target, &associatedObjectKey, @"添加的字符串属性", OBJC_ASSOCIATION_RETAIN_NONATOMIC); //获取关联对象
NSString *string = objc_getAssociatedObject(target, &associatedObjectKey);
NSLog(@"AssociatedObject = %@", string);
objc_setAssociatedObject的四个参数:
id object给谁设置关联对象。
const void *key关联对象唯一的key,获取时会用到。
id value关联对象。
objc_AssociationPolicy关联策略,有以下几种策略:
enum {
OBJC_ASSOCIATION_ASSIGN = ,
OBJC_ASSOCIATION_RETAIN_NONATOMIC = ,
OBJC_ASSOCIATION_COPY_NONATOMIC = ,
OBJC_ASSOCIATION_RETAIN = ,
OBJC_ASSOCIATION_COPY =
};
如果你熟悉OC,看名字应该知道这几种策略的意思了吧。
objc_getAssociatedObject的两个参数。
id object获取谁的关联对象。
const void *key根据这个唯一的key获取关联对象。
其实,你还可以把添加和获取关联对象的方法写在你需要用到这个功能的类的类别中,方便使用。
//添加关联对象
- (void)addAssociatedObject:(id)object{
objc_setAssociatedObject(self, @selector(getAssociatedObject), object, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
//获取关联对象
- (id)getAssociatedObject{
return objc_getAssociatedObject(self, _cmd);
}
注意:这里面我们把getAssociatedObject方法的地址作为唯一的key,_cmd代表当前调用方法的地址。
方法交换
+ (void)load{
//方法交换应该被保证,在程序中只会执行一次
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
//获得viewController的生命周期方法的selector
SEL systemSel = @selector(viewWillAppear:);
//自己实现的将要被交换的方法的selector
SEL swizzSel = @selector(swiz_viewWillAppear:);
//两个方法的Method
Method systemMethod = class_getInstanceMethod([self class], systemSel);
Method swizzMethod = class_getInstanceMethod([self class], swizzSel);
//首先动态添加方法,实现是被交换的方法,返回值表示添加成功还是失败
BOOL isAdd = class_addMethod(self, systemSel, method_getImplementation(swizzMethod), method_getTypeEncoding(swizzMethod));
if (isAdd) {
//如果成功,说明类中不存在这个方法的实现
//将被交换方法的实现替换到这个并不存在的实现
class_replaceMethod(self, swizzSel, method_getImplementation(systemMethod), method_getTypeEncoding(systemMethod));
}else{
//否则,交换两个方法的实现
method_exchangeImplementations(systemMethod, swizzMethod);
}
});
}
- (void)swiz_viewWillAppear:(BOOL)animated{
//这时候调用自己,看起来像是死循环
//但是其实自己的实现已经被替换了
[self swiz_viewWillAppear:animated];
NSLog(@"swizzle");
}
Runtime初识的更多相关文章
- Objective-C runtime初识
Objective-C Runtime Describes the macOS Objective-C runtime library support functions and data struc ...
- Runtime-b
感谢大神分享 依旧是网上很多runtime的资料,依旧是看不懂,,,这里给大家转化一下runtime,使它由隐晦难懂变得通俗易懂. (虽然截图和语言组织的有些凌乱,但是大家还是一点一点的阅读下去吧,可 ...
- 初识 Runtime
前言 之前在看一些第三方源码的时候,时不时的能碰到一些关于运行时相关的代码.于是乎,就阅读了一些关于运行时的文章,感觉写的都不错,写此篇文章为了记录一下,同时也重新学习一遍. Runtime简介 Ru ...
- 初识runtime
首先需要加上头文件#import<objc/runtime.h>和#Import<objc/message.h> 将A中的某个方法替换成B中某个方法,且没有任何的耦合 这里 ...
- Java初识
基础概念 特点: 完全面向对象,动态 解释性,简单.易移植,跨平台 安全健壮,高性能 多线程,分布式 三种核心机制: Java虚拟机 Java Virtual Machine 垃圾收集机制 Garba ...
- C#脚本引擎 CS-Script 之(一)——初识
最近在做新产品,这个产品需要满足不同项目对于系统的定制性数据处理需求,比如有的要统计一段时间内某开关打开关闭了多少次,有的要统计一段时间内空调的使用率,有的希望根据温度来控制空调的开还是关,有的则是希 ...
- XSS 自动化检测 Fiddler Watcher & x5s & ccXSScan 初识
一.标题:XSS 自动化检测 Fiddler Watcher & x5s & ccXSScan 初识 automated XSS testing assistant 二.引言 ...
- iOS 开发-- Runtime 1小时入门教程
1小时让你知道什么是Objective-C Runtime,并对它有一定的基本了解,可以在开发过程中运用自如. 三.Objective-C Runtime到底是什么东西? 简而言之,Objective ...
- 一个初学者的辛酸路程-初识Python-1
前言 很喜欢的一句话,与诸位共勉. 人的一切痛苦,本质上都是对自己无能的愤怒----王小波. 初识Python 一.它的爸爸是谁 首先,我们需要认识下面这位人物. 他是Python的创始人,吉多范罗苏 ...
随机推荐
- iis 中https修改主机名方法
来源地址: https://www.pianyissl.com/support/page/29
- C#之asp.net 及MVC 生成动态验证码:
C#之asp.net 及MVC 生成动态验证码: 1.生成验证码字符串 // 随机生成指定长度的验证码字符串private string RandomCode(int length) { string ...
- Docker容器的自动化监控实现
本文由 网易云 发布. 近年来容器技术不断成熟并得到应用.Docker作为容器技术的一个代表,目前也在快速发展中,基于 Docker的各种应用也正在普及,与此同时 Docker对传统的运维体系也带来 ...
- docker registry 私有仓库 安装配置、查询、删除
#++++++++++++++++++++++++++++++ #docker-registry 私有仓库 #搜索,下载register镜像 docker search registry docker ...
- php curl 伪造IP来源的实例代码
来源:http://www.jb51.net/article/31694.htm curl 它不但可以模仿用户登录,还可以模仿用户IP地址哦,为伪造IP来源,本实例仅供参考哦 //伪造ip ; $i ...
- webpack快速入门——CSS中的图片处理
1.首先在网上随便找一张图片,在src下新建images文件夹,将图片放在文件夹内 2.在index.html中写入代码:<div id="pic"></div& ...
- MVC4删除 pages引发的异常 System.Web.Optimization找不到引用
在MVC4的开发中,如果创建的项目为空MVC项目,那么在App_Start目录下没有BundleConfig.cs项的内容,在手动添加时在整个库中都找不到:System.Web.Optimizatio ...
- centos7系统的进程管理
使用top命令,实时查看后台的进程,会看到以下信息 官方的解释: us: user cpu time (or) % CPU time spent in user space sy: system cp ...
- webpack的安装与使用(单文件)
在安装 Webpack 前,你本地环境必须已安装nodejs. 可以使用npm安装,当然由于 npm 安装速度慢,也可以使用淘宝的镜像及其命令cnpm(npm install -g cnpm --re ...
- Sublime Text 乱码解决(Package Control 和 ConvertToUTF8插件安装)
Sublime Text的界面正如她的名字sublime一样,充满极客感觉的高大上,而且拥有强大的功能.但是她默认是不支持GBK编码的. 本来安装一个Package Control插件管理,再安装其他 ...