iOS-Runtime机制详解
一、简介
runtime是一套底层的纯c语言的API,我们写的代码在程序运行过程中都会被转化成runtime的C代码执行,例如[target doSomething];会被转化成objc_msgSend(target, @selector(doSomething))
二、类在runtime中的表示(可以使用command+shiift+o搜索runtime.h查看)
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; ///相关的定义 /// 描述类中的一个方法 typedef struct objc_method *Method; /// 实例变量 typedef struct objc_ivar *Ivar; /// 类别Category typedef struct objc_category *Category; /// 类中声明的属性 typedef struct objc_property *objc_property_t;
三、API的使用
//load方法会在类第一次加载的时候被调用 //调用的时间比较靠前,适合在这个方法里做方法交换 + (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"); } #pragma mark--实例变量 - (void)queryProperty{ unsigned int count; objc_property_t *propertyList = class_copyPropertyList([Person class], &count); ; i < count; i++) { objc_property_t pro = propertyList[i]; const char *key = property_getName(pro); NSString *keyString = [NSString stringWithUTF8String:key]; NSLog(@"%@",keyString); } } #pragma mark--成员变量 - (void)qyeryVar{ unsigned int count; Ivar *ivar = class_copyIvarList([Person class], &count); ; i < count; i++) { Ivar var = ivar[i]; const char *key = ivar_getName(var); NSString *keyString = [NSString stringWithUTF8String:key]; NSLog(@"%@",keyString); } } #pragma mark--方法遍历 - (void)querymethod{ unsigned int count; Method *methodList = class_copyMethodList([Person class], &count); ; i < count; i++) { Method method = methodList[i]; SEL select = method_getName(method); NSString *selectString = NSStringFromSelector(select); NSLog(@"%@",selectString); } }
四、应用场景--用runtime机制防止UIButton重复点击
1、UIControl的sendAction:to:forEvent:方法用于处理事件响应.如果我们在该方法的实现中, 添加针对点击事件的时间间隔相关的处理代码, 则能够做到在指定时间间隔中防止重复点击.
首先, 为UIButton添加一个Category:
@interface UIButton (CS_FixMultiClick) @property (nonatomic, assign) NSTimeInterval cs_acceptEventInterval; // 重复点击的间隔 @property (nonatomic, assign) NSTimeInterval cs_acceptEventTime; @end
2、Category不能给类添加属性, 所以以上的cs_acceptEventInterval和cs_acceptEventTime只会有对应的getter和setter方法, 不会添加真正的成员变量.如果我们不在实现文件中添加其getter和setter方法, 则采用 btn.cs_acceptEventInterval = 1; 这种方法尝试访问该属性会出错.
在实现文件中通过runtime的关联对象的方式, 为UIButton添加以上两个属性. 代码如下:
#import "UIControl+CS_FixMultiClick.h" #import <objc/runtime.h> @implementation UIButton (CS_FixMultiClick) // 因category不能添加属性,只能通过关联对象的方式。 static const char *UIControl_acceptEventInterval = "UIControl_acceptEventInterval"; - (NSTimeInterval)cs_acceptEventInterval { return [objc_getAssociatedObject(self, UIControl_acceptEventInterval) doubleValue]; } - (void)setCs_acceptEventInterval:(NSTimeInterval)cs_acceptEventInterval { objc_setAssociatedObject(self, UIControl_acceptEventInterval, @(cs_acceptEventInterval), OBJC_ASSOCIATION_RETAIN_NONATOMIC); } static const char *UIControl_acceptEventTime = "UIControl_acceptEventTime"; - (NSTimeInterval)cs_acceptEventTime { return [objc_getAssociatedObject(self, UIControl_acceptEventTime) doubleValue]; } - (void)setCs_acceptEventTime:(NSTimeInterval)cs_acceptEventTime { objc_setAssociatedObject(self, UIControl_acceptEventTime, @(cs_acceptEventTime), OBJC_ASSOCIATION_RETAIN_NONATOMIC); } // 在load时执行hook + (void)load { Method before = class_getInstanceMethod(self, @selector(sendAction:to:forEvent:)); Method after = class_getInstanceMethod(self, @selector(cs_sendAction:to:forEvent:)); method_exchangeImplementations(before, after); } - (void)cs_sendAction:(SEL)action to:(id)target forEvent:(UIEvent *)event { if ([NSDate date].timeIntervalSince1970 - self.cs_acceptEventTime < self.cs_acceptEventInterval) { return; } ) { self.cs_acceptEventTime = [NSDate date].timeIntervalSince1970; } [self cs_sendAction:action to:target forEvent:event]; } @end
3、使用
btn.cs_acceptEventInterval = ;
iOS-Runtime机制详解的更多相关文章
- IOS缓存机制详解
资料均来自互联网,转载时请务必以超链接形式标明文章 原始出处 .作者信息和本声明.否则将追究法律责任. 人魔七七:http://www.cnblogs.com/qiqibo/ 为什么要有缓存 应用需要 ...
- 【转】IOS缓存机制详解
人魔七七:http://www.cnblogs.com/qiqibo/ 为什么要有缓存 应用需要离线工作的主要原因就是改善应用所表现出的性能.将应用内容缓存起来就可以支持离线.我们可以用两种不同的缓存 ...
- iOS开发:详解Objective-C runTime
Objective-C总Runtime的那点事儿(一)消息机制 最近在找工作,Objective-C中的Runtime是经常被问到的一个问题,几乎是面试大公司必问的一个问题.当然还有一些其他问题也几乎 ...
- iOS的消息转发机制详解
iOS开发过程中,有一类的错误会经常遇到,就是找不到所调用的方法,当然这类问题比较好解决,给当前对象或其父类对象添加该方法即可,使得编译器在编译时能正确找到该方法:或者,还有另外的方法,由于Objec ...
- iOS开发——Block详解
iOS开发--Block详解 1. Block是什么 代码块 匿名函数 闭包--能够读取其他函数内部变量的函数 函数变量 实现基于指针和函数指针 实现回调的机制 Block是一个非常有特色的语法,它可 ...
- iOS开发者证书-详解
iOS开发者证书-详解/生成/使用 本文假设你已经有一些基本的Xcode开发经验, 并注册了iOS开发者账号. 相关基础 加密算法 现代密码学中, 主要有两种加密算法: 对称密钥加密 和 公开密钥加密 ...
- java面试题之----JVM架构和GC垃圾回收机制详解
JVM架构和GC垃圾回收机制详解 jvm,jre,jdk三者之间的关系 JRE (Java Run Environment):JRE包含了java底层的类库,该类库是由c/c++编写实现的 JDK ( ...
- Android应用AsyncTask处理机制详解及源码分析
1 背景 Android异步处理机制一直都是Android的一个核心,也是应用工程师面试的一个知识点.前面我们分析了Handler异步机制原理(不了解的可以阅读我的<Android异步消息处理机 ...
- Java SPI机制详解
Java SPI机制详解 1.什么是SPI? SPI 全称为 (Service Provider Interface) ,是JDK内置的一种服务提供发现机制.SPI是一种动态替换发现的机制, 比如有个 ...
- 【转载】Android应用AsyncTask处理机制详解及源码分析
[工匠若水 http://blog.csdn.net/yanbober 转载烦请注明出处,尊重分享成果] 1 背景 Android异步处理机制一直都是Android的一个核心,也是应用工程师面试的一个 ...
随机推荐
- C#中int32 的有效值范围
C#中int32 的有效值范围是[Int32.MinValue, Int32.MaxValue]中的整数,或者说是从 -2^16 到 2^16-1 之间的整数
- 将bat文件或exe程序注册成windows服务
命令行使用sc命令.关于sc命令的详解,请自行查看帮助(sc /?),在此只简单提及如何加入系统服务功能.加入服务:sc create ServiceName binPath= 路径 start= a ...
- [SOJ] Babelfish
Description You have just moved from Waterloo to a big city. The people here speak an incomprehensib ...
- 学习Sass(一)
一.什么是sass? 写过css的都知道,css是用来改变页面样式的.但它不是一种编程语言,没有变量,函数,继承等功能,只能一条条抒写样式语句很死板.在这个工具决定效率的时代,这是不能容忍的事情.怎样 ...
- java http url post json
import java.io.IOException; import java.io.InputStream; import java.io.OutputStreamWriter; import ja ...
- 免费SSL证书申请
https://buy.wosign.com/free/#apply 和 http://www.startssl.com/
- CodeForces 667C Reberland Linguistics
$dp$. 题意中有一个词组:$in$ $a$ $row$,是连续的意思.... 因此这题只要倒着$dp$一下就可以了.$f[i][0]$表示从$i$位置往后割两个能否割,$f[i][1]$表示从$i ...
- spring 加载多个资源文件
<bean id="propertyConfigurer" class="org.springframework.beans.factory.config.Prop ...
- 每天学习一点点...css...
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title> ...
- 在一个form里边同时执行搜索和 execl导出功能
一个form 分搜索 和 导出<form name="searchform" id="searchform" > <input type=&q ...