Objective-C 动态特性

本文转载自:https://onevcat.com/2012/04/objective-c-runtime/

动态特性

Objective-C 具有相当多的动态特性,基本的,有:

动态类型(Dynamic typing)
动态绑定(Dynamic binding)
动态加载(Dynamic loading)

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

动态特性基础

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
    //...
}

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

2.动态绑定

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

动态绑定所做的,即是在实例所属类确定后,将某些属性和相应的方法绑定到实例上。这里所指的属性和方法当然包括了原来没有在类中实现的,而是在运行时才需要的新加入的实现。在 Cocoa 层,我们一般向一个 NSObject 对象发送 -respondsToSelector: 或者 -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(前者添加实现,后者替换实现),来完成动态绑定的需求。

3.动态加载

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