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的一个核心,也是应用工程师面试的一个 ...
随机推荐
- java的return区别
return ;和return null的区别在于:前者当方法返回值为void时候,return ; 跳出方法. 后者当方法的返回值为object对象时,return null,跳出方法,返回值为空值 ...
- selenium2使用记录
安装 pip install selenium web phantomjs下载 :http://phantomjs.org/download.html 浏览器驱动下载:http://www.selen ...
- neovim的新体验
A. 缘由 vim下的CtrlP插件好用,但是当文件较多时,不能很快检索,时有卡死的情况发生.听说neovim引入了很多新的功能,例如异步处理,job管理等. B. 安装neovim1. Ubunt ...
- web字体图标的使用
今天给大家介绍一些web字体图标的下载和使用 一.WEB字体 1. 下载外部的字体图标的网站 font-awesome.com 2.CSS文件和font文件 3.html文档中使用外部字体 4.下载字 ...
- linux 发送带附件的邮件
一.mutt命令 1.安装 yum -y install mutt 2.使用 echo "mail content"|mutt -s "mail title" ...
- spring 加载多个资源文件
<bean id="propertyConfigurer" class="org.springframework.beans.factory.config.Prop ...
- ie Css Hack 特殊符号
Css Hack 特殊符号 (1)* :IE6/7都能识别*,标准浏览器不识别(2)_:只有IE6识别(3)!Important:IE6不识别,Firefox,IE7/8/9.chorme等主流浏览器 ...
- 实现table中checkbox复选框、以及判断checked是否被选中、js操作checkedbox选中
上图是实现效果. 下面贴代码 表的第一行也就是<th>中的代码,onclick事件是实现全选或者全不选效果. <th> <input id="allboxs&q ...
- KMP算法的实现
今天看到了一篇关于KMP算法的讲解的文章,很难得,讲得非常清楚.分享给大家,希望对大家有帮助.http://kb.cnblogs.com/page/176818/ 我自己基于这个讲解的内容作了一个实现 ...
- Oracle数据库创建数据库实例1
http://jingyan.baidu.com/article/ae97a646d128d5bbfd461d00.html