Objective-C的Runtime机制的应用示例总结
来源:互联网 发布:JavaScript什么是闭包 编辑:程序博客网 时间:2024/04/30 13:21
Objective-C是一门动态语言,不同于许多静态语言,例如C语言,只能在编译和链接阶段把程序运行的上下文做好,在运行期间,无法修改,缺少动态性。Objective-C的动态性,给开发者提供了一种在运行期,修改程序执行流程的机会,这要归功于其强大的Runtime机制。
这篇文章主要介绍,目前,Runtime机制在我们项目中的应用场景。
- 前言
ObjC语言中,runtime运行机制主要依赖于两个头文件
#include <objc/runtime.h>#include <objc/message.h>
其中runtime.h声明了一些类,实例变量操作相关的东西,message.h声明了一些消息发送相关的东西。
下面列举一下,runtime.h中常用的方法,以及它们的用法。
object_getClass: 获取一个对象所属的类object_getIvar: 读取一个对象中某个变量的值objc_getClass: 通过字符串获取到类Classclass_getSuperclass:获取一个类的父类class_getInstanceMethod:获取实例方法,返回Methodclass_getMethodImplementation:获取实例方法的实现class_getProperty: 获取某个类的属性class_addMethod: 增加方法。...等等
上面大部分列举了get相关的方法。runtime.h提供了获取(get),设置(set)(类的属性,实例变量,实例方法,类方法)的操作。
下面列举一下message.h的相关方法。
objc_msgSend: 给对象发送消息。objc_msgSendSuper: 向父类的发送消息
message.h类,主要提供了这两个关键的方法。
- 下面总结一下Runtime有哪些使用场景,并通过具体的代码,来说明各种使用场景如何运用Runtime。
总结了如下四种应用场景:
1.动态拼接URL 2.交换两个方法的实现(用自己的方法,替换系统的方法) 3.给系统已有的方法添加新的功能(不影响系统方法原来的功能) 4.消息转发中Runtime的应用
下面结合项目中使用到Runtime的地方进行说明,并列举了一些代码示例。
第一个使用场景:动态拼接URL
在做app开发的时候,肯定会遇到http请求,URL拼接的问题。初期大部分采取的方案是,采用NSString的格式化方法,自己手动去拼接URL. 例如:
NSMutableString *stringUrl = [NSMutableString stringWithFormat:@"http:mytest/host/code=%@",code]; [stringUrl appendString:@"&uin=45"]; [stringUrl appendString:@"&my=5"]; [stringUrl appendString:@"&your=6"];
这样写有几个问题:
1. 手动书写,很容易写错。
2. 如果很多类似的请求,都需要uin或者其他通用的参数,那么就会每个请求都需要写一遍。不满足OOP的特性。
3. 参数所代表的含义不够清晰,需要开发去猜测。
解决方法:
把url拼接,抽象成一个类。
上面的参数code, uin,my,your可以当成类的属性。通用的属性有uin,可以抽象出一个基类。实现方法如下:
CURLParamBase类
@interface CURLParamBase : NSObject@property (nonatomic, strong) NSString *uin;@end
需要构造的请求类XXX,如下定义:
@interface CURLParamXXX : CURLParamBase@property (nonatomic, strong) NSString* my;@property (nonatomic, strong) NSString* your;@end
使用:定义CURLParamXXX的类的实例,使用Runtime来获取属性名和属性的value来拼接参数,如下:
unsigned int propertyCount; //获取所有属性 objc_property_t *properties = class_copyPropertyList(class, &propertyCount); for (unsigned int i = 0; i < propertyCount; i++) { objc_property_t property = properties[i]; const char *propertyName = property_getName(property);//获取实例变量,某个属性的值 object_getInstanceVariable(self,propertyName,&value); }
-第二个使用场景:交换两个方法的实现
使用newSEL方法名,新的newIMP实现,替换原有的origSEL方法及其实现。
实现代码如下,代码中添加了相关的注释。
/** **新方法的实现替换旧方法 **成功:返回YES, **失败:返回NO. */BOOL replaceMethodNewImpl(Class c, SEL origSEL, SEL newSEL, IMP newIMP){ //新方法已经存在 if ([c instancesRespondToSelector:newSEL]) { return YES; } //旧方法 Method origMethod = class_getInstanceMethod(c, origSEL); //先把新方法加入到class的方法列表中 if (!class_addMethod(c, newSEL, newIMP, method_getTypeEncoding(origMethod))) { //如果加入失败,直接返回 NSLog(@"Failed to add method:%@ on %@",NSStringFromSelector(newSEL),c); return NO; } else { //如果加入成功 Method newMethod = class_getInstanceMethod(c, newSEL); //给original添加新的实现 //有可能失败失败原因:(for example, the class already contains a method implementation with that name). if (class_addMethod(c, origSEL, method_getImplementation(newMethod), method_getTypeEncoding(origMethod))) { //新方法的实现替换成旧方法的实现。 class_replaceMethod(c, newSEL, method_getImplementation(origMethod), method_getTypeEncoding(newMethod)); } else { //交换实现 method_exchangeImplementations(origMethod, newMethod); } } return YES;}
- 第三个使用场景:给系统已有的方法添加功能
例如给系统的UIView下的方法drawRect,添加NSLog的功能,这个可以参考上面说明的第三种使用场景。变化的只是,新方法的实现要去调用老的方法的实现。在override_drawRect方法中,调用drawRect方法。代码如下:
- (void)override_drawRect:(CGRect)r{ // 调用旧的实现。因为它们已经被替换了 [self override_drawRect: r]; NSLog(@"rect = %@",NSStringFromCGRect(r));}
- 第四使用场景:消息转发
开发过程中,经常遇到unrecognized selector sent to instance 0x87 Terminating app due to uncaught exception
NSInvalidArgumentException’, 这个问题。
这是什么原因?直观上看,是系统没有处理某个消息。
情况分两种,第一,接受消息的对象错了。第二,对象没错,发送的消息不对。
消息转发的整个流程如下图所示:
总体来说,就是给某个实例,发送某个消息。
首先如果没找到响应方法,系统会给你转到其他方法的机会,只要实现了resolveInstanceMethod方法即可。
其次,如果没有实现,你还可以修改接受消息的对象,让其他对象去响应消息,覆盖方法forwardingTargetSelector即可。
如果这两者都没做,你还可以在forwardInvocation,做自己的逻辑处理,是否继续处理消息。
这篇文章不具体讨论这个转发流程的细节,只是为了说明runtime在整个过程中的运用。
Runtime在其中的运用。
在resolveInstanceMethod方法中,可以通过覆盖resolveInstanceMethod方法,如下:
+ (BOOL)resolveInstanceMethod:(SEL)sel { NSString *selectorString = NSStringFromSelector(sel); if ([selectorString hasPrefix:@"set"]) { class_addMethod(self, sel, (IMP)mySetMethod, "v@:@"); } else { class_addMethod(self, sel, (IMP)myGetMethod, "@@:"); } return YES;}
代码中,本类实现了动态的添加选择子,把选择子关联到自己定义的方法上,这样在访问某个属性的时候,会动态的访问我们自己定义的方法。在消息转发的流程中,实现了动态添加方法实现的能力。
综上所述,Runtime提供了Objective-C强大的动态性,可谓是方便灵活,运用起来能做很多事情,也欢迎大家补充说明在你们的项目中,Runtime都为你们做了什么。
(完)
- Objective-C的Runtime机制的应用示例总结
- Objective-C中runtime机制的应用
- Objective-C中runtime机制的应用
- 深入理解Objective-C的Runtime机制
- Objective-C总Runtime的 消息机制
- 深入理解Objective-C的Runtime机制
- 深入理解Objective-C的Runtime机制
- 深入理解Objective-C的Runtime机制
- 深入理解Objective-C的Runtime机制
- 深入理解Objective-C的Runtime机制
- 深入理解Objective-C的Runtime机制
- 初步学习objective-c的Runtime机制
- Objective-C runtime的常见应用
- Objective-C runtime的常见应用
- Objective-C runtime的常见应用
- Objective-C的Runtime
- Objective-C 的 Runtime
- Objective C的runtime
- android 获取全局context
- poj-1222 EXTENDED LIGHTS OUT
- IOS调节屏幕的光线感应度
- 阿里云安装svn失败,提示telnet: connect to address Connection refused的解决办法
- java 面象对象
- Objective-C的Runtime机制的应用示例总结
- 简历书写原则
- js表单验证
- UGUI ScrollRect滚动优化:无限循环利用Item
- 开放-封闭原则
- html知识复习
- ubuntu12.04 下安装配置samba
- poj-3185 The Water Bowls
- Mysql 里CHAR和VARCHAR的最大长度及一些注意事项