【IOS】深入Objective-C的动态特性(转)-程序员宅基地

技术标签: java  移动开发  设计模式  

转载至:http://www.onevcat.com/2012/04/objective-c-runtime/

Objective-C具有相当多的动态特性,基本的,也是经常被提到和用到的有动态类型(Dynamic typing),动态绑定(Dynamic binding)和动态加载(Dynamic loading)。

这些动态特性都是在Cocoa程序开发时非常常用的语言特性,而在这之后,OC在底层也提供了相当丰富的运行时的特性,比如枚举类属性方法、获取方法实现等等。虽然在平常的Cocoa开发中这些较底层的运行特性基本用不着,但是在某些情况下如果你知道这些特性并合理加以运用的话,往往能事半功倍~

动态特性基础

1、动态类型

即运行时再决定对象的类型。这类动态特性在日常应用中非常常见,简单说就是id类型。id类型即通用的对象类,任何对象都可以被id指针所指,而在实际使用中,往往使用introspection来确定该对象的实际所属类:

1 id obj = someInstance;  
2 if ([obj isKindOfClass:someClass])  
3 {
4     someClass *classSpecifiedInstance = (someClass *)obj;
5     // Do Something to classSpecifiedInstance which now is an instance of someClass
6     //...
7 }

 

-isMemberOfClass: 是 NSObject 的方法,用以确定某个 NSObject 对象是否是某个类的成员。与之相似的为-isKindOfClass:,可以用以确定某个对象是否是某个类或其子类的成员。这两个方法为典型的introspection方法。在确定对象为某类成员后,可以安全地进行强制转换,继续之后的工作。

2、动态绑定

基于动态类型,在某个实例对象被确定后,其类型便被确定了。该对象对应的属性和响应的消息也被完全确定,这就是动态绑定。在继续之前,需要明确Objective-C中消息的概念。由于OC的动态特性,在OC中其实很少提及“函数”的概念,传统的函数一般在编译时就已经把参数信息和函数实现打包到编译后的源码中了,而在OC中最常使用的是消息机制。调用一个实例的方法,所做的是向该实例的指针发送消息,实例在收到消息后,从自身的实现中寻找响应这条消息的方法。

动态绑定所做的,即是在实例所属类确定后,将某些属性和相应的方法绑定到实例上。这里所指的属性和方法当然包括了原来没有在类中实现的,而是在运行时才需要的新加入的实现。在Cocoa层,我们一般向一个NSObject对象发送-respondsToSelector:或者-instancesRespondToSelector:等来确定对象是否可以对某个SEL做出响应,而在OC消息转发机制被触发之前,对应的类的+resolveClassMethod:和+resolveInstanceMethod:将会被调用,在此时有机会动态地向类或者实例添加新的方法,也即类的实现是可以动态绑定的。一个例子:

 1 void dynamicMethodIMP(id self, SEL _cmd)  
 2 {
 3     // implementation ....
 4 }
 5 
 6 
 7 
 8 //该方法在OC消息转发生效前被调用
 9 + (BOOL) resolveInstanceMethod:(SEL)aSEL
10 { 
11     if (aSEL == @selector(resolveThisMethodDynamically)) {
12         //向[self class]中新加入返回为void的实现,SEL名字为aSEL,实现的具体内容为dynamicMethodIMP class_addMethod([self class], aSEL, (IMP) dynamicMethodIMP, “v@:”);
13         return YES;
14     }
15     return [super resolveInstanceMethod:aSel];
16 }  

 

当然也可以在任意需要的地方调用class_addMethod或者method_setImplementation(前者添加实现,后者替换实现),来完成动态绑定的需求。

3、动态加载

根据需求加载所需要的资源,这点很容易理解,对于iOS开发来说,基本就是根据不同的机型做适配。最经典的例子就是在Retina设备上加载@2x的图片,而在老一些的普通屏设备上加载原图。随着Retina iPad的推出,和之后可能的Retina Mac的出现,这个特性相信会被越来越多地使用。

深入运行时特性

基本的动态特性在常规的Cocoa开发中非常常用,特别是动态类型和动态绑定。由于Cocoa程序大量地使用Protocol-Delegate的设计模式,因此绝大部分的delegate指针类型必须是id,以满足运行时delegate的动态替换(在Java里这个设计模式被叫做Strategy?不是很懂Java,求纠正)。而Objective-C还有一些高级或者说更底层的运行时特性,在一般的Cocoa开发中较为少见,基本被运用与编写OC和其他语言的接口上。但是如果有所了解并使用得当的话,在Cocoa开发中往往可以轻易解决一些棘手问题。

这类运行时特性大多由/usr/lib/libobjc.A.dylib这个动态库提供,里面包括了对于类、实例成员、成员方法和消息发送的很多API,包括获取类实例变量列表,替换类中的方法,为类成员添加变量,动态改变方法实现等,十分强大。完整的API列表和手册可以在这里找到。虽然文档开头表明是对于Mac OS X Objective-C 2.0适用,但是由于这些是OC的底层方法,因此对于iOS开发来说也是完全相同的。

一个简单的例子,比如在开发Universal应用或者游戏时,如果使用IB构建了大量的自定义的UI,那么在由iPhone版转向iPad版的过程中所面临的一个重要问题就是如何从不同的nib中加载界面。在iOS5之前,所有的UIViewController在使用默认的界面加载时(init或者initWithNibName:bundle:),都会走-loadNibNamed:owner:options:。而因为我们无法拿到-loadNibNamed:owner:options的实现,因此对其重载是比较困难而且存在风险的。因此在做iPad版本的nib时,一个简单的办法是将所有的nib的命名方式统一,然后使用自己实现的新的类似-loadNibNamed:owner:options的方法将原方法替换掉,同时保证非iPad的设备还走原来的loadNibNamed:owner:options方法。使用OC运行时特性可以较简单地完成这一任务。

代码如下,在程序运行时调用+swizze,交换自己实现的loadPadNibNamed:owner:options和系统的loadNibNamed:owner:options,之后所有的loadNibNamed:owner:options消息都将会发为loadPadNibNamed:owner:options,由自己的代码进行处理。

 1 +(BOOL)swizze {
 2     Method oldMethod = class_getInstanceMethod(self, @selector(loadNibNamed:owner:options:));
 3     if (!oldMethod) {
 4         return NO;
 5     }
 6     Method newMethod = class_getInstanceMethod(self, @selector(loadPadNibNamed:owner:options:));
 7     if (!newMethod) {
 8         return NO;
 9     }
10     method_exchangeImplementations(oldMethod, newMethod);
11     return YES;
12 }  

 

loadPadNibNamed:owner:options的实现如下,注意在其中的loadPadNibNamed:owner:options由于之前已经进行了交换,因此实际会发送为系统的loadNibNamed:owner:options。以此完成了对不同资源的加载。

 1 -(NSArray *)loadPadNibNamed:(NSString *)name owner:(id)owner options:(NSDictionary *)options {
 2     NSString *newName = [name stringByReplacingOccurrencesOfString:@"@pad" withString:@""];
 3     newName = [newName stringByAppendingFormat:@"@pad"];
 4     //判断是否存在
 5     NSFileManager *fm = [NSFileManager defaultManager];
 6     NSString* filepath = [[NSBundle mainBundle] pathForResource:newName ofType:@”nib”];
 7     //这里调用的loadPadNibNamed:owner:options:实际为为交换后的方法,即loadNibNamed:owner:options:
 8     if ([fm fileExistsAtPath:filepath]) {
 9         return [self loadPadNibNamed:newName owner:owner options:options];
10     } else {
11         return [self loadPadNibNamed:name owner:owner options:options]; 
12     }
13 } 

 

当然这只是一个简单的例子,而且这个功能也可以通过别的方法来实现。比如添加UIViewController的类别来重载init,但是这样的重载会比较危险,因为你UIViewController的实现你无法完全知道,因此可能会由于重载导致某些本来应有的init代码没有覆盖,从而出现不可预测的错误。当然在上面这个例子中重载VC的init的话没有什么问题(因为对于VC,init的方法会被自动转发为loadNibNamed:owner:options,因此init方法并没有做其他更复杂的事情,因此只要初始化VC时使用的都是init的话就没有问题)。但是并不能保证这样的重载对于其他类也是可行的。因此对于实现未知的系统方法,使用上面的运行时交换方法会是一个不错的选择~

转载于:https://www.cnblogs.com/DannyApple/p/4119934.html

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/weixin_33978016/article/details/93615242

智能推荐

试题8 算法训练 P0103(从键盘输入一个大写字母,要求改用小写字母输出。)_从键盘输入一个大写字母,要求改用小写字母输出,fun函数-程序员宅基地

文章浏览阅读1.8k次。题目:从键盘输入一个大写字母,要求改用小写字母输出。输入  A输出  a分析思路:主要考虑ASII码值的相互转化a: 对应ASII值为:97A: 对应ASII值为:65大小写之间相差32方法1代码:#include <iostream>#include <algorithm>using namespace std;const int n=2;int main(){char A[n];cin>>A;char B[n];_从键盘输入一个大写字母,要求改用小写字母输出,fun函数

NVIDIA JETSON XAVIER NX 安装tensorflow_jetson agx xavier安装h5py-程序员宅基地

文章浏览阅读3.7k次。一 拿到板子,查看jetpack版本head -n 1 /etc/nv_tegra_release二 安装anaconda替代品,miniforge提示:个人感觉miniforge用起来和anaconda没什么区别NVIDIA Jetson Xavier使用的aarch64架构是没法使用anaconda的,虽然可以自己编译,但是conda指令是没法编译到aarch64架构上运行的。不过有替代品,名字是miniforge。miniforge与miniconda的区..._jetson agx xavier安装h5py

UWB什么要对工厂园区进行精准定位_uwb融合差分定位技术 厂区边界-程序员宅基地

文章浏览阅读460次。工业改革以来,国家一直致力于工业制造的不断发展,所以不管是“工业4.0”还是“智能制造2025”都是智慧工厂实现高效生产、传统转型的目标,为了更好的完成传统工业向智能化工业的迈进,UWB无论是从定位精度、安全性、抗干扰、功耗等多种技术来分析,无疑是目前最理想的智慧工厂人员定位技术之一。UWB定位面临哪些企业管理难点1.企业安全信息管理由于企业性质,考虑到员工生产安全、商业保密和工作效率,解决员工进入工作区域因禁止携带手机导致的调度及通信难题;2.企业人员、车辆来往管控难点对于大型企业几百甚至几千_uwb融合差分定位技术 厂区边界

md0和md1linux软raid,软RAID管理命令mdadm详解-程序员宅基地

文章浏览阅读2.8k次。mdadm是linux下用于创建和管理软件RAID的命令,是一个模式化命令。但由于现在服务器一般都带有RAID阵列卡,并且RAID阵列卡也很廉价,且由于软件RAID的自身缺陷(不能用作启动分区、使用CPU实现,降低CPU利用率),因此在生产环境下并不适用。但为了学习和了解RAID原理和管理,因此仍然进行一个详细的讲解:一、创建模式选项:-C专用选项:-l 级别-n 设备个数-a {yes|no} ..._md1482.x叫0

解决csv文件打开是乱码的问题_csv文件输入是乱码-程序员宅基地

文章浏览阅读4w次,点赞20次,收藏35次。将数据库的数据导出为CSV文件,之后用excel打开,出现了乱码情况,真心无奈,感觉都要理转文了!╮(╯﹏╰)╭原因:CSV是用UTF-8编码的,而EXCEL是ANSI编码,由于编码方式不一致导致出现乱码。解决办法:用记事本打开CSV文件-----&gt;文件------&gt;另存为------&gt;点编码------&gt;选择ANSI-----&gt;保存,然后用excel打开就不会..._csv文件输入是乱码

EasyUI datetimebox月份和按钮实现中文显示以及格式化处理-程序员宅基地

文章浏览阅读861次,点赞2次,收藏2次。EasyUI默认calendar、datebox、datetimebox的组件显示都是英文的。如下。修改为显示中文结果如下。 具体实现如下。重点是script里面<!DOCTYPE html><html> <head> <meta charset="utf-8"> <title></title> <link rel="stylesheet" type="text/css" hr..._easyui datetimebox

随便推点

2024年气候变化、旅游景观与经济发展国际会议(ICTLED 2024)-程序员宅基地

文章浏览阅读665次,点赞15次,收藏17次。向ICTLED 2024提交的所有全文都可以用英语书写,并将发送给至少两名评审员,并根据原创性、技术或研究内容或深度、正确性、与会议的相关性、贡献和可读性进行评估。ICTLED 2024所有被接受的论文将在会议记录中发表,并提交给Scopus、EI Compendex、CPCI、CNKI、Google Scholar进行索引。的科研学者、技术人员及相关人员提供一个共享科研成果和前沿技术,了解学术发展趋势,拓宽研究思路,加强学术研究和探讨,促进学术成果产业化合作的平台。拒稿),越早投稿越早收到文章结果。

使用mqtt协议 硬件对接_mqtt怎么对接硬件-程序员宅基地

文章浏览阅读5k次。Shaocheng.LiToggle main menu visibility首页关于MQTT 协议和 mosquitto2015-08-11 Network 12792MQTT 介绍Mosquitto安全性3.1. SSL/TLS3.2. WebSockets with SSL/TLSlibmosquitto 库4.1. 获取库版本4.2. 初始化和清除4.3..._mqtt怎么对接硬件

店盈通:拼多多一个店铺推几个产品最好_拼多多一户能做几个产品吗?-程序员宅基地

文章浏览阅读1.5k次。  在拼多多店铺上面上架商品,那肯定也需要了解到底维持多少的数量合适。那么拼多多店铺商品数量多少合适?越多越好吗?下面店盈通给大家讲述一下。  1、输入账号名,点击保存,选择已配置的店铺,或者在开始页面配置店铺。  2、输入商品地址,可以同时输入不同的拼多多商品地址。  3、填写需要填写的相关信息。  4、选中要上传的商品,点击上传商品。如果有传错了可以点击删除商品。  5、上传成功后,您可以在上传的页面中找到您上传的商品,或者您可以点击删除商品选项,选择要删除的商品。  ._拼多多一户能做几个产品吗?

curl断点续传杂记-程序员宅基地

文章浏览阅读1.3k次。为了解决 HTTP/1.0 存在的资源浪费的问题, HTTP/1.1 优化为默认长连接模式。_curl断点续传

apk开发学习!目前最全的《Android面试题及解析》!深度好文_apk分析的试题-程序员宅基地

文章浏览阅读102次。何为成长?成长是指自我提升,一方面是本身的个人能力,另一方面是社会对你的认可度。最终,程序员的职位和薪水都能在成长中得以体现。很多人对成长有误解,在他们眼中,随着工作年限的提高,成长是理所当然的事情,这其实是一个误区。两个程序员同时工作3年,难道他们两个的成长就完全一样吗?其实是不一样的。很多岗位在招聘的时候都要求3年以上工作经验,这个3年工作经验是指持续成长的三年,而不是指浑浑噩噩混日子的三年。下面举个通俗易懂的例子,大家一定能理解。面试经历1.偶然看到知乎的内推帖,投了个简历,下午hr姐姐ca._apk分析的试题

Kodi ——6 Controls (13)6.13 Text Box_text to display goes here.核心语句-程序员宅基地

文章浏览阅读275次。6.13 Text Box 文本框用于显示XBMC多页文字。您可以选择的位置,大小,外观。6.13.1 Example My first text box control 80 60 250 200 true FFFFFFFF font13 FFFFFFFF 13 200 !Control_text to display goes here.核心语句

推荐文章

热门文章

相关标签