前言:

         Objective C的runtime技术功能非常强大,能够在运行时获取并修改类的各种信息,包括获取方法列表、属性列表、变量列表,修改方法、属性,增加方法,属性等等,本文对相关的几个要点做了一个小结。

目录:

(1)使用class_replaceMethod/class_addMethod函数在运行时对函数进行动态替换或增加新函数

(2)重载forwardingTargetForSelector,将无法处理的selector转发给其他对象

(3)重载resolveInstanceMethod,从而在无法处理某个selector时,动态添加一个selector

(4)使用class_copyPropertyList及property_getName获取类的属性列表及每个属性的名称

(5) 使用class_copyMethodList获取类的所有方法列表

(6) 总结

(1)在运行时对函数进行动态替换 : class_replaceMethod

使用该函数可以在运行时动态替换某个类的函数实现,这样做有什么用呢?最起码,可以实现类似windows上hook效果,即截获系统类的某个实例函数,然后塞一些自己的东西进去,比如打个log什么的。

示例代码:

IMP orginIMP;
NSString * MyUppercaseString(id SELF, SEL _cmd)
{NSLog(@"begin uppercaseString"); NSString *str = orginIMP (SELF, _cmd);(3) NSLog(@"end uppercaseString"); return str; } -(void)testReplaceMethod { Class strcls = [NSString class]; SEL oriUppercaseString = @selector(uppercaseString); orginIMP = [NSStringinstanceMethodForSelector:oriUppercaseString]; (1) IMP imp2 = class_replaceMethod(strcls,oriUppercaseString,(IMP)MyUppercaseString,NULL);(2) NSString *s = "hello world"; NSLog(@"%@",[s uppercaseString]]; }

执行结果为:

begin uppercaseString

end uppercaseString

HELLO WORLD

这段代码的作用就是

(1)得到uppercaseString这个函数的函数指针存到变量orginIMP中

(2)将NSString类中的uppercaseString函数的实现替换为自己定义的MyUppercaseString

(3) 在MyUppercaseString中,先执行了自己的log代码,然后再调用之前保存的uppercaseString的系统实现,这样就在系统函数 执行之前加入自己的东西,后面每次对NSString调用uppercaseString的时候,都会打印出log来

与class_replaceMethod相仿,class_addMethod可以在运行时为类增加一个函数。

(2)当某个对象不能接受某个selector时,将对该selector的调用转发给另一个对象:- (id)forwardingTargetForSelector:(SEL)aSelector

forwardingTargetForSelector是NSObject的函数,用户可以在派生类中对其重载,从而将无法处理的selector转 发给另一个对象。还是以上面的uppercaseString为例,如果用户自己定义的CA类的对象a,没有uppercaseString这样一个实例 函数,那么在不调用respondSelector的情况下,直接执行[a performSelector:@selector"uppercaseString"],那么执行时一定会crash,此时,如果CA实现了 forwardingTargetForSelector函数,并返回一个NSString对象,那么就相对于对该NSString对象执行了 uppercaseString函数,此时就不会crash了。当然实现这个函数的目的并不仅仅是为了程序不crash那么简单,在实现装饰者模式时,也 可以使用该函数进行消息转发。

示例代码:

 @interface CA : NSObject-(void)f;@end@implementation CA- (id)forwardingTargetForSelector:(SEL)aSelector{if (aSelector == @selector(uppercaseString)) { return@"hello world"; } }

测试代码:

CA *a = [CA new];NSString * s = [a performSelector:@selector(uppercaseString)];NSLog(@"%@",s);

测试代码的输出为:HELLO WORLD

ps: 这里有个问题,CA类的对象不能接收@selector(uppercaseString),那么如果我在 forwardingTargetForSelector函数中用class_addMethod给CA类增加一个uppercaseString函数, 然后返回self,可行吗?经过试验,这样会crash,此时CA类其实已经有了uppercaseString函数,但是不知道为什么不能调用,如果此 时new一个CA类的对象,并返回,是可以成功的。

(3)当某个对象不能接受某个selector时,向对象所属的类动态添加所需的selector

+ (BOOL) resolveInstanceMethod:(SEL)aSEL

这个函数与forwardingTargetForSelector类似,都会在对象不能接受某个selector时触发,执行起来略有差别。前者的目 的主要在于给客户一个机会来向该对象添加所需的selector,后者的目的在于允许用户将selector转发给另一个对象。另外触发时机也不完全一 样,该函数是个类函数,在程序刚启动,界面尚未显示出时,就会被调用。

在类不能处理某个selector的情况下,如果类重载了该函数,并使用class_addMethod添加了相应的selector,并返回YES,那么后面forwardingTargetForSelector 就不会被调用,如果在该函数中没有添加相应的selector,那么不管返回什么,后面都会继续调用 forwardingTargetForSelector,如果在forwardingTargetForSelector并未返回能接受该 selector的对象,那么resolveInstanceMethod会再次被触发,这一次,如果仍然不添加selector,程序就会报异常

代码示例一:@implementation CAvoid dynamicMethodIMP(id self, SEL _cmd){   printf("SEL %s did not exist\n",sel_getName(_cmd)); } + (BOOL) resolveInstanceMethod:(SEL)aSEL { if (aSEL == @selector(t)) { class_addMethod([selfclass], aSEL, (IMP) dynamicMethodIMP, "v@:"); return YES; } return [superresolveInstanceMethod:aSEL]; } @end 测试代码: CA * ca = [CA new] [ca performSelector:@selector(t)]; 

执行结果

SEL t did not exist

示例代码二:@implementation CAvoid dynamicMethodIMP(id self, SEL _cmd)
{printf("SEL %s did not exist\n",sel_getName(_cmd)); } + (BOOL) resolveInstanceMethod:(SEL)aSEL { return YES; } - (id)forwardingTargetForSelector:(SEL)aSelector { if (aSelector == @selector(uppercaseString)) { return @"hello world"; } } 测试代码 : a = [[CA alloc]init]; NSLog(@"%@",[a performSelector:@selector(uppercaseString)];

该测试代码的输出为:HELLO WORLD

对于该测试代码,由于a没有uppercaseString函数,因此会触发resolveInstanceMethod,但是由于该函数并没有添加selector,因此运行时发现找不到该函数,会触发

forwardingTargetForSelector 函数,在forwardingTargetForSelector函数中,返回了一个NSString "hello world",因此会由该string来执行uppercaseString函数,最终返回大写的hello world。

示例代码三:
@implementation CA+ (BOOL) resolveInstanceMethod:(SEL)aSEL
{return  YES;
}
- (id)forwardingTargetForSelector:(SEL)aSelector
{return nil; } 测试代码: 1 a = [[CA alloc]init]; 2 NSLog(@"%@",[a performSelector:@selector(uppercaseString)];

这段代码的执行顺序为:

(1):首先在程序刚执行,AppDelegate都还没有出来时,resolveInstanceMethod就被触发,

(2):等测试代码执行时,forwardingTargetForSelector被调用

(3):由于forwardingTargetForSelector返回了nil,因此运行时还是找不到uppercaseString selector,这时又会触发resolveInstanceMethod,由于还是没有加入selector,于是会crash。

(4) 使用class_copyPropertyList及property_getName获取类的属性列表及每个属性的名称

u_int               count;
objc_property_t*    properties= class_copyPropertyList([UIView class], &count);
for (int i = 0; i < count ; i++) { const char* propertyName = property_getName(properties[i]); NSString *strName = [NSString stringWithCString:propertyName encoding:NSUTF8StringEncoding]; NSLog(@"%@",strName); } 以上代码获取了UIView的所有属性并打印属性名称, 输出结果为: skipsSubviewEnumeration viewTraversalMark viewDelegate monitorsSubtree backgroundColorSystemColorName gesturesEnabled deliversTouchesForGesturesToSuperview userInteractionEnabled tag layer _boundsWidthVariable _boundsHeightVariable _minXVariable _minYVariable _internalConstraints _dependentConstraints _constraintsExceptingSubviewAutoresizingConstraints _shouldArchiveUIAppearanceTags

(5) 使用class_copyMethodList获取类的所有方法列表

获取到的数据是一个Method数组,Method数据结构中包含了函数的名称、参数、返回值等信息,以下代码以获取名称为例:

u_int               count;
Method*    methods= class_copyMethodList([UIView class], &count);
for (int i = 0; i < count ; i++) { SEL name = method_getName(methods[i]); NSString *strName = [NSString stringWithCString:sel_getName(name) encoding:NSUTF8StringEncoding]; NSLog(@"%@",strName); } 代码执行后将输出UIView所有函数的名称,具体结果略。 其他一些相关函数: 1.SEL method_getName(Method m) 由Method得到SEL 2.IMP method_getImplementation(Method m) 由Method得到IMP函数指针 3.const char *method_getTypeEncoding(Method m) 由Method得到类型编码信息 4.unsigned int method_getNumberOfArguments(Method m)获取参数个数 5.char *method_copyReturnType(Method m) 得到返回值类型名称 6.IMP method_setImplementation(Method m, IMP imp) 为该方法设置一个新的实现

(6)总结

总而言之,使用runtime技术能做些什么事情呢?

可以在运行时,在不继承也不category的情况下,为各种类(包括系统的类)做很多操作,具体包括:

1.   增加增加函数:class_addMethod
增加实例变量:class_addIvar
增加属性:@dynamic标签,或者class_addMethod,因为属性其实就是由getter和setter函数组成
增加Protocol:class_addProtocol (说实话我真不知道动态增加一个protocol有什么用,-_-!!)2。  获取  获取函数列表及每个函数的信息(函数指针、函数名等等):class_getClassMethod method_getName ...
获取属性列表及每个属性的信息:class_copyPropertyList property_getName
获取类本身的信息,如类名等:class_getName class_getInstanceSize
获取变量列表及变量信息:class_copyIvarList
获取变量的值3. 替换 将实例替换成另一个类:object_setClass 将函数替换成一个函数实现:class_replaceMethod 直接通过char *格式的名称来修改变量的值,而不是通过变量

转载于:https://www.cnblogs.com/fangjie123/p/5609439.html

RunTime技术总结相关推荐

  1. Objective C运行时(runtime)技术总结,好强大的runtime

    前言:          Objective C的runtime技术功能非常强大,能够在运行时获取并修改类的各种信息,包括获取方法列表.属性列表.变量列表,修改方法.属性,增加方法,属性等等,本文对相 ...

  2. iOS学习之Runtime(二)

    前面已经介绍了Runtime系统的概念.作用.部分技术点和应用场景,这篇将会继续学习Runtime的其他知识. 一.Runtime技术点之类/对象的关联对象 关联对象不是为类/对象添加属性或者成员变量 ...

  3. SDK无埋点技术在百分点的探索和实践

    2016-10-11 唐星 移动开发前线 移动开发前线 移动开发前线 微信号 bornmobile 功能介绍 专注于分享移动开发前沿和一线技术. 本文为『移动前线』群在9月21日的分享总结整理而成,转 ...

  4. Springboot源码分析之内嵌tomcat源码分析

    Springboot源码是内嵌tomcat的,这个和完整的tomcat还是不同. 内嵌tomcat的源码在tomcat-embed-core等3个jar包里 展开tomcat-embed-core的c ...

  5. 高性能Sqlite存储模型对象解密

    前言 首先写这篇文章之前祝大家元旦快乐,然后自我介绍一下,我叫吴海超(WHC)在iOS领域有丰富的开发架构经验Github以后我也会以文章的形式分享具有实战意义的文章给大家,希望能够给大家有所帮助. ...

  6. [转]CS的顶级会议和期刊

    原文地址:   http://blog.csdn.net/noter/archive/2009/08/05/4414168.aspx CS的顶级会议和期刊 SCI或SCIE收录的本学科刊物清单请登陆 ...

  7. swift 组件化_打造完备的iOS组件化方案:如何面向接口进行模块解耦?

    作者 | 黑超熊猫zuik,一个修行中的 iOS 开发,喜欢搞点别人没搞过的东西,钻研过逆向工程.VIPER 架构和组件化. 关于组件化的探讨已经有不少了,在之前的文章 iOS VIPER架构实践(三 ...

  8. 安全容器的发展与思考

    作者: 王旭 蚂蚁金服资深技术专家,kata containers 架构委员会成员 刘奖 阿里云操作系统资深技术专家 本文根据云栖大会容器专场演讲内容整理,关注阿里巴巴云原生公众号,回复"安 ...

  9. tomcat6 优化初步

    必须要配置环境变量 才可以使用 Apache Portable Runtime 技术 http://tomcat.apache.org/download-native.cgi 如果使用的是解压方式的t ...

最新文章

  1. 2022-2028年中国柴油行业市场研究及前瞻分析报告
  2. 真机IOS8.3以上的文件夹共享
  3. opencv画直线,矩形
  4. 信息系统项目管理师-信息文档与配置管理核心知识点思维脑图
  5. python3协程 queue_使用gevent库+queue模块实现多协程爬虫,提高爬取效率!
  6. 利用python爬虫(案例8)--今天就是吃X我也要搞到有道
  7. android的帧布局,七、Android帧布局FrameLayout和霓虹灯效果
  8. ffmpeg: error while loading shared libraries: libavdevice.so.52
  9. hp打印机怎么连接电脑_hp打印机驱动怎么安装 惠普打印机驱动程序安装方法【详解】...
  10. Apache配置文件httpd.conf详解
  11. 小米路由器mini WOL网络唤醒插件
  12. RemoteDesktopManager和微软远程桌面管理器RDCman
  13. 5gh掌上云计算认证不通过_2018年阿里云ACP云计算认证多少分通过,怎么报名,如何参加考试...
  14. 谷歌浏览器在编辑器中打不开_如何在Google文档中使用编辑器聊天
  15. 计算机财务管理系统的建立,第13章 计算机财务管理系统的建立.ppt
  16. 信息化与数字化的区别
  17. 干货 | 如何搭建小型视频点播网站
  18. Dubbo过滤器原理解析
  19. python 杀死线程_如何杀死正在运行的线程
  20. python任务分配问题_【python3】任务分配问题

热门文章

  1. vue 循环 递归组件_Vue一个案例引发的递归组件的使用
  2. 手机端网站排名优化需注意哪些细节?
  3. 网络请求可以返回数据的网站_实例解析|Python加解密VIP网站反爬请求头实现数据爬取...
  4. java求最小步数,使数组值相等的最小步数 Minimum Moves to Equal Array Elements
  5. 设计sample语言的词法分析器_重现木兰编程语言(十)——比较、循环,一个语法设计特色
  6. python yaml配置文件_python读取yaml配置文件
  7. 转载 SharedPreference.Editor的apply和commit方法异同
  8. RBF神经网络——直接看公式,本质上就是非线性变换后的线性变化(RBF神经网络的思想是将低维空间非线性不可分问题转换成高维空间线性可分问题)...
  9. python dpkt 解析 pcap 文件
  10. scrapy 6023 telnet查看爬虫引擎相关状态