`
lizaochengwen
  • 浏览: 643765 次
  • 性别: 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新手及老手参考

    多年iOS开发经验总结

    ios开发常用到的API,适用于ios开发者,iOS开发经验总结

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

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

    iOS开发进阶-唐巧.pdf

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

    iOS开发进阶-完整版

    唐巧的《iOS开发进阶》,希望对一些初级的程序员有用

    2018秋招iOS面试总结

    2018秋招iOS面试总结 2018秋招iOS面试总结 2018秋招iOS面试总结 2018秋招iOS面试总结

    iOS开发视频教程

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

    精通iOS开发源码地址

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

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

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

    学习ios(必看经典)牛人40天精通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开发人员提高自己的开发水平

    iOS开发之美光盘源代码

    iOS开发之美光盘源代码,由于视频太大,没有上传。

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

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

    总结最近ios开发心得

    总结最近ios开发心得,挺好,挺完善。。

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

    精通IOS开发第7版的归档文件,源代码,包括资源文件,在apress下载的最新的

    IOS开发总结

    ios开发总结,适合新手看

    ios开发技巧总结.docx

    ios开发 iOS开发涵盖了许多方面,以下是一些关键的开发技巧和最佳实践: 熟悉Swift或Objective-C:Swift是苹果推荐的iOS应用开发语言,它具有安全性、高性能和现代编程特性。虽然Objective-C仍可使用,但新项目...

Global site tag (gtag.js) - Google Analytics