`
lizaochengwen
  • 浏览: 662562 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

iOS开发总结 ObjCRuntimeGuide小记

 
阅读更多
版本和平台

Runtime System对于Objective-C来说就好比是它的操作系统/运行平台,它使得Objective-C代码能跑得起来。

相对于C/C++来说,Objective-C尽可能地把一些动作推迟到运行时来执行,即尽可能动态地做事情。因此,它不仅需要一个编译器,还需要一个运行时环境来执行编译后的代码。

这里会讨论到NSObject类,Objective-C程序如何与Runtime System交互,运行时动态地加载新类,发消息给其它对象,以及运行时如何获取对象信息。

Runtime System分为Legacy和Modern两个版本,一般来说,我们现在用的都是Modern版本。

Modern版本的Runtime System有一个显著的特征就是“non-fragile”,即父类的成员变量的布局发生改变时,子类不需要重新编译。此外,还支持为声明的属性进行合成操作(即@property和@synthesis)。

与Runtime System交互

Objective-C程序和Runtime System在三个不同层次进行交互:通过Objective-C源码;通过NSObject定义的函数;以及通过直接调用runtime functions。

通常来讲,Runtime System都是在幕后工作,我们需要做的就是编写Objective-C代码,然后编译。编译器会为我们创建相应的数据结构和函数调用来实现语言的动态特性。这些数据结构保存着类、Category定义和Protocol声明中所能找到的信息,包括成员变量模板、selectors,以及其它从源码中提取到的信息。最主要的Runtime函数是用来发送消息的,它由源码中的消息表达式激发。

Cocoa中大部分对象都是NSObject的子类(NSProxy是一个例外),继承了NSObject的方法。因此在这个继承体系中,子类可以根据需求重新实现NSObject定义的一些函数,实现多态和动态性,比如description方法。

一些NSObject定义的方法只是简单地询问Runtime System获得信息,使得对象可以进行自省(introspection),比如一些类方法:用来确定类类型的isKindOfClass:,确定对象在继承体系中的位置的isMemberOfClass:,判断一个对象是否能接收某个特定消息的respondsToSelector:,判断一个对象是否遵循某个协议的conformsToProtocol:,以及提供方法实现地址的methodForSelector:。这些方法让一个对象可以进行自省(introspect about itself)。

Runtime System是一个动态共享库,位于/usr/include/objc,拥有一套公共的接口,由一系列函数和数据结构组成。开发人员可以使用纯C调用一些函数来做编译器做的事情,或者扩展Runtime System,为开发环境制作一些工具等等。尽管一般情况下,编写Objective-C并不需要了解这些内容,但有时候会很有用。所有的函数都在Objective-C Runtime Reference有文档化信息。

发送消息是Objective-C程序中最经常出现的表达式,而该表达式最终会被转换成objc_msgSend函数调用。

比如一个消息表达式[receiver message]会被转换成objc_msgSend(receiver, selector),如果有参数则为objc_msgSend(receiver, selector, arg1, arg2, …)。

消息只有到运行时才会和函数实现绑定起来:首先objc_msgSend在receiver中查找selector对应的函数实现;然后调用函数过程,将receiving object(即this指针)和参数传递过去;最后,返回函数的返回值。

发送消息的关键是编译器为类和对象创建的结构,包含两个主要元素,一个是指向superclass的指针,另一个是类的dispatch table,该dispatch table中的表项将selector和对应的函数入口地址关联起来。

当一个对象被创建时,内存布局中的第一个元素是指向类结构的指针,isa。通过isa指针,一个对象可以访问它的类结构,进而访问继承的类结构。示例图可参见:ObjCRuntimeGuide第14页。

当向一个对象发送消息时,objc_msgSend先通过isa指针在类的dispatch table中查找对应selector的函数入口地址,如果没有找到,则沿着class hierarchy(继承体系)寻找,直到NSObject类。这就是在运行时选择函数实现,用OOP的行话来说,就是动态绑定。

为了加速发送消息的速度,Runtime System为每个类创建了一个cache,用来缓存selector和对应函数入口地址的映射。

当objc_msgSend找到对应的函数实现时,它除了传递函数参数,还传递了两个隐藏参数:receiving object和selector。之所以称之为隐藏参数,是因为这两个参数在源代码中没有显示声明,但还是可以通过self和_cmd来访问。

当一个消息要被发送给某个对象很多次的时候,可以直接使用methodForSelector:来进行优化,比如下述代码:

//////////////////////////////////////////////////////////////   
void (*setter)(id, SEL, BOOL);  
int i;  
  
setter = (void (*)(id, SEL, BOOL))[target  
     methodForSelector:@selector(setFilled:)];  
for ( i = 0; i < 1000, i++ )   
     setter(targetList[i], @selector(setFilled:), YES);  
//////////////////////////////////////////////////////////////  

其中,methodForSelector:是由Cocoa Runtime System提供的,而不是Objective-C本身的语言特性。这里需要注意转换过程中函数类型的正确性,包括返回值和参数,而且这里的前两个参数需要显示声明为id和SEL。

方法的动态决议

有时候我们想要为一个方法动态地提供实现,比如Objective-C的@dynamic指示符,它告诉编译器与属性对应的方法是动态提供的。我们可以利用resolveInstanceMethod:和resolveClassMethod:分别为对象方法和类方法提供动态实现。

一个Objective-C方法本质上是一个拥有至少两个参数(self和_cmd)的C函数,我们可以利用class_addMethod向一个类添加一个方法(虽然文档没写,但个人认为就是向dispatch table添加一个键值对)。

比如对于下面的函数:

//////////////////////////////////////////////////////////////   
void dynamicMethodIMP(id self, SEL _cmd) {  
     // implementation ….   
}  
//////////////////////////////////////////////////////////////  

我们可以利用resolveInstanceMethod:将它添加成一个方法(比如叫resolveThisMethodDynamically):

//////////////////////////////////////////////////////////////   
@implementation MyClass  
+ (BOOL)resolveInstanceMethod:(SEL)aSEL  
{  
     if (aSEL == @selector(resolveThisMethodDynamically)) {  
          class_addMethod([self class], aSEL, (IMP) dynamicMethodIMP, "v@:");  
          return YES;  
     }  
     return [super resolveInstanceMethod:aSEL];  
}  
@end  
//////////////////////////////////////////////////////////////  

动态决议和发送消息并不冲突,在消息机制起作用之前,一个类是有机会动态决议一个方法的。当respondsToSelector:或者instancesRespondToSelector:被激活时,dynamic method resolver会优先有个机会为这个selector提供一份实现。如果实现了resolveInstanceMethod:,对于不想动态决议而想让其遵循消息转发机制的selectors,返回NO即可。

Objective-C程序可以在运行时链接新的类和category。动态加载可以用来做很多不同的事情,比如System Preferences里头各种模块就是动态加载的。尽管有运行时函数可以动态加载Objective-C模块(objc/objc-load.h中的objc_loadModules),但Cocoa的NSBundle类提供了更方便的动态加载接口。

消息转发

向一个对象发送它不处理的消息是一个错误,不过在报错之前,Runtime System给了接收对象第二次的机会来处理消息。在这种情况下,Runtime System会向对象发一个消息,forwardInvocation:,这个消息只携带一个NSInvocation对象作为参数——这个NSInvocation对象包装了原始消息和相应参数。

通过实现forwardInvocation:方法(继承于NSObject),可以给不响应的消息一个默认处理方式。正如方法名一样,通常的处理方式就是转发该消息给另一个对象:

//////////////////////////////////////////////////////////////   
- (void)forwardInvocation:(NSInvocation *)anInvocation  
{  
     if ([someOtherObject respondsToSelector:[anInvocation selector]])  
          [anInvocation invokeWithTarget:someOtherObject];  
     else  
          [super forwardInvocation:anInvocation];  
}  
//////////////////////////////////////////////////////////////  

对于不识别的消息(在dispatch table中找不到),forwardInvocation:就像一个中转站,想继续投递或者停止不处理,都由开发人员决定。

类型编码

为了支持Runtime System,编译器将返回值类型、参数类型进行编码,相应的编译器指示符是@encode。

比如,void编码为v,char编码为c,对象编码为@,类编码为#,选择符编码为:,而符合类型则由基本类型组成,比如

typedef struct example {  
     id     anObject;  
     char *aString;  
     int anInt;  
} Example;  
编码为{example=@*i}。


属性声明

当编译器遇到属性声明时,它会生成一些可描述的元数据(metadata),将其与相应的类、category和协议关联起来。存在一些函数可以通过名称在类或者协议中查找这些metadata,通过这些函数,我们可以获得编码后的属性类型(字符串),复制属性的attribute列表(C字符串数组)。因此,每个类和协议的属性列表我们都可以获得。

与类型编码类似,属性类型也有相应的编码方案,比如readonly编码为R,copy编码为C,retain编码为&等。

通过property_getAttributes函数可以后去编码后的字符串,该字符串以T开头,紧接@encode type和逗号,接着以V和变量名结尾。比如:

@property char charDefault;

描述为:Tc,VcharDefault

而@property(retain)ididRetain;

描述为:T@,&,VidRetain

Property结构体定义了一个指向属性描述符的不透明句柄:typedef struct objc_property *Property;。

通过class_copyPropertyList和protocol_copyPropertyList函数可以获取相应的属性数组:

objc_property_t *class_copyPropertyList(Class cls, unsigned int *outCount) 
objc_property_t *protocol_copyPropertyList(Protocol *proto, unsigned int *outCount) 
通过property_getName函数可以获取属性名称。

通过class_getProperty和protocol_getProperty可以相应地根据给定名称获取到属性引用:

objc_property_t class_getProperty(Class cls, const char *name) 
objc_property_t protocol_getProperty(Protocol *proto, const char *name, BOOL isRequiredProperty, BOOL isInstanceProperty) 
通过property_getAttributes函数可以获取属性的@encode type string:

const char *property_getAttributes(objc_property_t property)

以上函数组合成一段示例代码:

@interface Lender : NSObject {  
     float alone;  
}  
@property float alone;  
@end  
  
id LenderClass = objc_getClass("Lender");  
unsigned int outCount, i;  
objc_property_t *properties = class_copyPropertyList(LenderClass, &outCount);  
for (i = 0; i < outCount; i++) {  
     objc_property_t property = properties[i];  
     fprintf(stdout, "%s %s\n", property_getName(property), property_getAttributes(property));  
}  


原文路径:http://www.linuxidc.com/Linux/2012-02/54989.htm
分享到:
评论

相关推荐

    iOS 开发总结

    这份"iOS开发总结"文档可能涵盖了从基础概念到高级技术的各种知识点,旨在帮助开发者提升技能和解决问题。 首先,iOS开发主要基于Apple的Swift编程语言,这是一种现代化、高性能的语言,具有安全性和易于读写的特点...

    《iOS开发零基础入门教程》(40集)

    资源名称:《iOS开发零基础入门教程》(40集)资源目录:【】传智播客《iOS开发零基础入门教程》1.1【】传智播客《iOS开发零基础入门教程》1.2【】传智播客《iOS开发零基础入门教程》1.3【】传智播客《iOS开发零基础...

    IOS开发指南(第5版) pdf下载地址

    《iOS开发指南(第5版)》是一本深入探讨iOS应用程序开发的专业书籍,旨在帮助开发者从零基础到熟练掌握Apple的移动操作系统上的应用构建过程。该书第五版更新了最新的开发技术和工具,确保读者能够使用最新的Xcode和...

    多年iOS开发经验总结

    多年iOS开发经验的总结中提到了几个关键技术点,包括对UILabel的文本属性设置、多线程操作、数据类型比较、日期时间计算等。 首先,关于UILabel的文本属性设置,通过使用NSMutableAttributedString类,可以对...

    iOS开发进阶-完整版

    根据提供的信息,我们可以推断出这是一本关于iOS开发进阶的书籍,作者为唐巧。虽然提供的部分内容似乎并不是实际的章节内容,但从标题、描述和标签中,我们可以推测本书可能涵盖的一些关键知识点。 ### iOS开发进阶...

    精通IOS开发 第7版 归档文件

    《精通iOS开发 第7版》是一本深入探讨iOS应用程序开发的专业书籍,其归档文件包含了丰富的源代码和资源文件,旨在帮助开发者深入了解并熟练掌握iOS平台的开发技术。这一版本聚焦于最新的iOS版本,提供了全面的更新和...

    iOS开发进阶-唐巧.pdf

    第一部分介绍iOS 开发的常用工具,第二部分介绍iOS开发中的一些常见的实践经验,第三部分介绍iOS 开发中涉及的原理。 如果把成为iOS 开发高手的过程比作武侠小说中的修炼过程的话,工具、实践和理论的学习就分别对应...

    iOS开发视频教程

    资源名称:iOS开发视频教程资源目录:【】iOS开发视频教程-第01讲-iOS历史介绍【】iOS开发视频教程-第02讲-XCode安装【】iOS开发视频教程-第03讲-UIView_PPT【】iOS开发视频教程-第04讲-UILabel【】iOS开发视频教程-...

    《iOS开发项目化入门教程》源代码

    《iOS开发项目化入门教程》源代码是一份针对初学者的宝贵资源,旨在通过实际项目的实践,帮助开发者快速掌握iOS应用程序开发的基础技能。这个压缩包包含了一系列与iOS开发相关的源代码文件,这些文件反映了iOS应用从...

    iOS开发进阶篇-成为一个iOS开发高手

    在iOS开发领域,掌握进阶技术是提升个人技能的关键步骤,这将使你从众多开发者中脱颖而出。"iOS开发进阶篇-成为一个iOS开发高手"这份资料正为此目标提供了全面的指导。它深入探讨了iOS开发的核心概念和技术,旨在...

    精通iOS开发源码地址

    精通iOS开发源码下载地址,这个本书简直太棒了,是我买过的性价比最高的一本书。

    IOS开发环境搭建和简单实例

    在进行iOS开发之前,开发环境的搭建是开发者必须经历的初始步骤。本文将详细介绍如何搭建iOS开发环境,并提供一些简单的开发实例来帮助初学者快速入门。 ### iOS开发环境搭建 #### 注册Apple ID 为了下载开发所需...

    学习ios(必看经典)牛人40天精通iOS开发的学习方法

    这是一套从一个对iOS开发感兴趣的学员到iOS开发高手的系统、专业的课程体系。以培养企业开发真正需要的人才为目标,每个知识点都用案例来讲解。也适合想提升技能的已从事iOS开发的工作人员以最短时间内提升技能的...

    IOS官方开发手册

    《iOS官方开发手册》是苹果公司为iOS应用开发者提供的权威指南,它涵盖了从入门到精通的所有关键知识点。作为iOS开发的基石,这份手册是每个开发者不可或缺的参考资料。下面,我们将详细探讨其中的主要内容。 首先...

    高仿微信,iOS应用开发模板.zip ios 开发模板

    高仿微信,iOS应用开发模板.zip ios 开发模板。高仿微信,iOS应用开发模板.zip ios 开发模板。高仿微信,iOS应用开发模板.zip ios 开发模板。高仿微信,iOS应用开发模板.zip ios 开发模板。高仿微信,iOS应用开发...

    总结iOS开发代码实践总结iOS开发技巧共16页.pdf

    这份"总结iOS开发代码实践总结iOS开发技巧共16页.pdf"的文档,显然是一份宝贵的资源,它涵盖了多个方面的知识点,旨在帮助开发者提升其iOS应用开发技能。以下是基于该文件标题和描述可能包含的一些关键知识点的详细...

    iOS开发进阶

    作者唐巧,本书定位于帮助那些iOS开发人员提高自己的开发水平

    IOS开发

    iOS 开发概述 iOS 开发是指使用 Objective-C 语言在 Mac 系统上使用 Xcode 开发工具进行移动应用程序开发的过程。下面将从环境需求、环境搭建、开发语言三个方面对 iOS 开发进行详细介绍。 一、环境需求 iOS 开发...

Global site tag (gtag.js) - Google Analytics