深入Objective-C的动态特性 Runtime

《深入Objective-C的动态特性 Runtime》

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

这些动态特性都是在Cocoa程序开发时非常常用的语言特性,而在这之后,OC在底层也提供了相当丰富的运行时的特性,比如枚举类属性方法、获取方法实现等等。

下面就对每一种特性进行具体的介绍:

1. 动态类型

即运行时再决定对象的类型,如id类型。
id类型即通用的对象类,任何对象都可以被id指针所指,实际使用中,往往使用introspection来确定该对象的实际所属类:

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

SEL类型
Objective-C在编译的时候,会根据方法的名字 (包括参数序列),生成一个用来区分这个方法的唯一的一个标示(ID),这个标示(ID)就是SEL类型的,在运行时候是通过方法的标示来查找方法的。只要方法的名字(包括参数序列)相同,那么它们的 ID都是相同的。可以通过@select()指示符获得方法的标示。SEL mydraw =@select(draw);

NSSelectorFromString(NSString*);根据方法名得到方法标识

(NSString*)NSStringFromSelector(SEL);得到SEL类型的方法名

动态类型识别常用方法

-(BOOL)isKindOfClass:classObj 是否是classObj类或其子类

-(BOOL)isMemberOfClass:classObj是否是classObj的实例

-(BOOL)respondsTosSelector:selector 类中是否有这个方法

NSClassFromString(NSString*);由字符串得到类对象

NSStringFromClass([类名 Class]);由类名得到字符串

Class rectClass= [Rectangle class];通过类名得到类对象

Class aClass =[anObject class];通过实例得到类对象

if([obj1 class]== [obj2 class])判断是不是相同类的实例
(实际上静态类型因为其固定性和可预知性而使用的更加广泛。静态类型是强类型,而动态类型属于弱类型。运行时决定接受者。)

2、动态绑定

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

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

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

//该方法在OC消息转发生效前被调用
+ (BOOL) resolveInstanceMethod:(SEL)aSEL
 { if (aSEL == @selector(resolveThisMethodDynamically))
 {
 //向[self class]中新加入返回为void的实现,SEL名字为aSEL,实现的具体内容为dynamicMethodIMP
 class_addMethod([self class], aSEL, (IMP) dynamicMethodIMP, “v@:”);
return YES;
 }
 return [super resolveInstanceMethod:aSel];
 } 

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

消息转发机制:

  1. 在objective-c中,一个对象内否调用指定的方法不是由编译器决定而是由运行时决定,这被称作是方法的动态绑定。
  1. 在objective-c里,对象不调用方法,而是接收消息,消息 表达式为: [reciver message];运行时系统首先确定接收者的类型(动态类型识别),然 后根据消息名在类的方法列表里选择相依的方法执行,所 以在源代码里消息也称为选择器(selector)

  2. 消息函数的作用:

– 首先通过第一个参数的receiver,找到它的isa 指针,然 后在isa 指向的Class 对象中使用第二个参数selector 查 找方法;

– 如果没有找到,就使用当前Class 对象中的新的isa 指针 到上一级的父类的Class 对象中查找;

– 当找到方法后,再依据receiver 的中的self 指针找到当前 的对象,调用当前对象的具体实现的方法(IMP),然后传 递参数,调用实现方法。

– 假如一直找到NSObject 的Class 对象,也没有找到你调 用的方法,就会报告不能识别发送消息的错误。

  1. Objetive-C中的Method结构

struct objc_method{

SEL method_name;//方法名

char *method_types; //方法地址

IMP method_imp; //方法地址(IMP)

};

typedefobjc_method Method;

  1. 什么是IMP

– IMP是”implementation”的缩写,它是objetive-C 方法 (method)实现代码块的地址,类似函数指针,通过它可以 直接访问任意一个方法。免去发送消息的代价。

  1. 获取方法的IMP

– -(IMP)methodForSelector:(SEL)aSelector;

SEL print_sel =NSSelectorFromString(@“print:”);//获得SEL IMP imp=[person methodForSelector:print_sel];//得到IMP imp(person,print_sel,@“*********”);//通过IMP直接调用方法 等效调用:[person print_sel:@“*********”];

– imp的第一参数是对象自己(self),第二参数是方法标示, 第三个是方法的参数

3、动态加载

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

4、深入运行时特性

Objective-C还有一些更高级的运行时特性,这类运行时特性大多由/usr/lib/libobjc.A.dylib这个动态库提供,里面包括了对类、实例成员、成员方法和消息发送的很多API,包括获取类实例变量列表,替换类中的方法,为类成员添加变量,动态改变方法实现等,十分强大。完整的API列表.

The Runtime

Objective-C是一门简单的语言,95%是C。只是在语言层面上加了些关键字和语法。真正让Objective-C如此强大的是它的运行时。它很小但却很强大。它的核心是消息分发。

Messages

执行一个方法,有些语言,编译器会执行一些额外的优化和错误检查,因为调用关系很直接也很明显。但对于消息分发来说,就不那么明显了。在发消息前不必知道某个对象是否能够处理消息。你把消息发给它,它可能会处理,也可能转给其他的Object来处理。一个消息不必对应一个方法,一个对象可能实现一个方法来处理多条消息。

在Objective-C中,消息是通过objc_msgSend()这个runtime方法及相近的方法来实现的。这个方法需要一个target,selector,还有一些参数。理论上来说,编译器只是把消息分发变成objc_msgSend来执行。比如下面这两行代码是等价的。

[array insertObject:foo atIndex:5];
objc_msgSend(array, @selector(insertObject:atIndex:), foo, 5);

博客学习:
https://www.cnblogs.com/myios/p/3573313.html
https://blog.csdn.net/kuizhang1/article/details/20856091?utm_source=blogxgwz7

    原文作者:toro宇
    原文地址: https://www.jianshu.com/p/24f261672dba
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞