• iOS开发之Runtime机制深入解析


    本篇主要讲述在 OC 开发中主要涉及到的运行时机制:

    运行时的工作:

     运行时在 OC 中的工作:OC 语言的设计模式决定了尽可能的把程序从编译和链接时推迟到运行时。只要有可能,OC 总是使用动态的方式来解决问题。这意味着 OC 语言不仅需要一个编译器,同时也需要一个运行时系统来执行编译好的代码。这儿的运行时系统扮演的角色类似于 OC 语言的操作系统,OC 基于该系统来工作。

     运行时的简单应用:

     OC 2.0运行时系统参考库描述了OC 运行库的数据结构和函数接口。程序可以通过这些接口来和 OC 运行时系统交互。例如:增加一个类或者方法,或者获得所有类的定义列表等。

     运行时的两个版本:

     OC 运行时系统有两个版本,早期版本主要应用于 OC1.0中,现行版本用于 OC2.0中,在早期版本中,如果改变了类中实例变量的布局,就必须重新编译该类的所有子类。在现行版本中,如果改变了类中实例变量的布局,无需重新编译该类的任何子类。早起版本一般用于 Max OS X 系统中32位程序,此外可视为全部是现行版本。

     交互途径:

        1、通过 OC 源代码:

            当编译 OC 类和方法时,编译器为实现语句动态特性将自动创建一些数据结构和函数。运行时系统的主要功能就是根据源代码中的表达式发送消息。

        2、通过 Foundation 框架中类 NSObject 的方法:

            Cocoa 程序中绝大部分类都是 NSObject 类的子类,所以大部分都继承了 NSObject 类的方法,因而继承了 NSObject类的行为。然而某些情况下,NSObject 类仅仅定义了完成某件事情的模板,而没有提供所有需要的代码,某些 NSObject 的方法只是简单的从运行时系统中获得信息,从而允许对象进行一定程度的自我检查。如:class 返回对象的类:isKindOfClass:和 isMemberOfClass:则检查对象是否在指定的类继承体系中;respondsToSelector:检查对象能够相应指定的消息;conformsToProtocol:检查对象是否实现了指定协议类的方法;methodForSelector:则返回指定方法实现的地址。

        3、直接通过调用运行时系统的函数:

            运行时系统是一个公开接口的动态库,由一些数据结构和函数的集合组成,这些数据结构和函数的声明头文件在/usr/include/objc 中。这些函数支持用纯 C 的函数来实现和 OC 同样的功能。浪游一些函数构成了 NSObject 类方法的基础。这些函数使得访问运行时系统接口和提供开发工具成为可能。尽管大部分情况下他们在 OC 程序中不是必须的,但是有时候对于 OC 这样的程序来说某些函数是非常有用的。

     运行时的消息机制:

        1、获得方法地址:

            避免动态绑定的唯一方法就是取得方法的地址,并且直接像函数调用一样调用它。当一个方法会被联系调用很多次,而且您希望节省每次调用方法都要发送消息的开销时,使用方法地址来调用方法就显得很有效。

            利用 NSObject 类中的 methodForSelector:方法,可以获得一个指向方法实现的指针,并可以使用该指针直接调用方法实现。methodForSelector:返回的指针和赋值的变量类型必须完全一致,包括方法的参数类型和返回值类型都在类型识别的考虑范围中。

            在指定的消息被重复发送很多次时,避免动态绑定将减少大部分消息的开销。

        2、objc_msgSend 函数

            在 OC 中,消息是直到运行的时候才和方法实现绑定的,编译器会把一个消息表达式转换成一个对消息函数objc_msgSend 的调用。该函数有两个主要参数:消息接收者和消息对应的方法名字(也就是方法的选标)。

            objc_msgSend(receiver, selector),同时接收消息中的任意数目的参数:objc_msgSend(receiver, selector, arg1, arg2,...)

            该消息函数做了动态绑定所需要的一切;找到对应的方法实现-->将消息接收者对象和参数传递给找到的方法实现-->将方法实现的返回值作为该函数的返回值返回 

            

            当对象收到消息时,消息函数首先根据该对象的 isa 指针找到该对象所对应的类的方法表,并从表中寻找该消息对应的方法选标,如果找不到,objc_msSend 将从父类中找,知道 NSObject 类。一旦找到了选标,objc_msgSend 则以消息接收者对象为参数调用,调用该选标对应的方法实现。这就是在运行时系统中选择方法实现的方式。在面向对象编程中一般称作方法和消息动态绑定的过程。

            为了加快消息的处理过程,运行时系统通常会将使用过的方法选标和方法实现的地址放入缓存中。每个类都有一个独立的缓存,同时包括继承的方法和在该类中定义的方法。消息函数会首先检查消息接收者对象对应的类的缓存(理论上,如果一个方法被使用过一次,那么它很可能被再次使用)。如果在缓存中已经有了需要的方法选标,则消息仅仅比函数调用慢一点点。如果程序运行了足够长的时间,几乎每个消息都能在缓存中找到方法实现。程序运行时,缓存也将随着新的消息的增加而增加。

        3、使用隐藏的参数

            当 objc_msgSend 找到方法对应的实现时,它将直接调用该方法的实现,并将消息中所有的参数都传递给方法实现,同时,还将传递两个隐藏的参数:

                1、接受消息的对象:可以通过 self 来引用消息接收者对象

                2、方法选标:通过选标_cmd 来引用方法本身。

            尽管这些参数没有被显示声明,但在源代码中仍然可以引用它们。

     动态方法解析:

        1、动态方法解析:

            @dynamic property name; 表示编译器需动态的生成该属性对应的方法。

            可以通过实现 resolveInstanceMethod:和 resolveClassMethod:来动态的实现给定选标的对象方法或者类方法。

            OC 方法可以认为是至少有两个参数 self 和_cmd 的 C 函数。可以通过 class_addMethod 方法将一个函数加入到类的方法中,如下:

             

             void dynamicMethodIMP(id self, SEL _cmd) {

                // implementation...

             }

             + (BOOL)resolveInstanceMethod:(SEL)sel {

                 if (sel == @selector(resolveThisMethodDynamically)) {

                 class_addMethod([self class], sel, (IMP) dynamicMethodIMP, "v@:");

                 return YES;

             }

                 return [super resolveInstanceMethod:sel];

             }

            在进入消息转发机制之前,respondsToSelector:和 instancesRespondToSelector:会被首先调用。可以在这两个方法中为传进来的选标提供一个 IMP。如果您实现了 resolveInstanceMethod:方法但是仍然希望正常的消息转发机制进行,只需要返回 NO 就可以了。

        2、动态加载

            OC 程序可以在运行时链接和载入新的类和范畴类。新载入的类和在程序启动时载入的类并没有区别。

            动态加载可以用在很多地方,例如,系统配置中的模块就是被动态加载的。

            应用场景:

                在 Cocoa 环境中,动态加载一般被用来对应用程序进行定制。可以在运行时加载其他程序员编写的模块(和 interface Build载入定制的调色板以及系统配置程序载入定制的模块类似)。这些模块通过许可的方式扩展了自身的程序,而无需自己来定义或者实现。自己提供了框架,二其他程序员提供了实现。

     消息转发:

         1、消息转发

            如果一个对象收到一条无法处理的消息,运行时系统会在抛出错误前,给该对象发送一条 forwardInvocation:消息,该消息的唯一参数是个 NSInvocation 类型的对象,该对象封装了原始的消息和消息的参数。所以可以实现 forwardInvocation: 方法来对不能处理的消息做一些默认的处理,也可以以其它的某种方式来避免错误被抛出。

            当一个对象没有响应的方法实现而无法响应某消息时,运行时系统将通过 forwardInvocation: 消息通知该对象。每个对象都从 NSObject 类中集成了 forwardInvocation: 方法。然而,NSObject 中的方法实现只是简单的调用了 doerNotRecognizeSelector:。 通过实现自己的 forwardInvocation: 方法可以在该方法实现中将消息转发给其他对象。

            消息可以通过 invokeWithTarget:方法来转发:

            

             - (void)forwardInvocation:(NSInvocation *)anInvocation {

                 if ([someOtherObject respondsToSelector:[anInvocation selector]])

                     [anInvocation invokeWithTarget:someOtherObject];

                 else

                     [super forwardInvocation:anInvocation];

             }

         

            forwardInvocation:方法就像一个不能识别的消息的分发中心,将这些消息转发给不同接收对象。它可以将一个消息翻译成另外一个消息,或者简单的“吃掉”某些消息,因此没有响应也没有错误。forwardInvocation:方法也可以对不同的消息提供同样的相应,这一切都取决于方法的具体实现。该方法所提供是将不通的对象链接到消息链的能力。

         

            注意:forwardInvocation:方法只有在消息接受对象中无法正常响应消息时才会被调用。所以,如果您希望您的对象将 clickBtn:消息转发给其他对象,您的对象不能有 clickBtn:方法。否则,forwardInvocation:将不可能会被调用。

         

         2、消息转发和多重继承

            消息转发很像集成,并且可以用来在 OC 程序中模拟多重继承。一个对象通过转发来响应消息,看起来就像该对象从别的类那里借来了或者“继承”了方法实现一样。通过 forwardInvocation: 方法将一个类中的消息转发给另一个类.

         3、消息代理对象

         4、消息转发和类继承

  • 相关阅读:
    树莓派使用一些技巧总结
    在Win8上安装pyinstaller打包python成为可执行文件
    DOM对象(js对象)与jq对象
    jQuery初体验
    jQuery的入口函数
    二、Java面向对象(8)_继承思想——继承关系
    二、Java面向对象(7)_封装思想——判断点和圆的关系
    二、Java面向对象(7)_封装思想——构造器和setter方法选用
    二、Java面向对象(7)_封装思想——this关键字
    二、Java面向对象(7)_封装思想——JavaBean规范
  • 原文地址:https://www.cnblogs.com/Jepson1218/p/5324289.html
Copyright © 2020-2023  润新知