IOS -执行时 (消息传递再探究)

一 消息查找优化
	至此。我们已经明确了Objective-c中大致的消息传递过程,我们发现假设每次函数调用都经历上面的过程(。那函数调用的效率就会非常低,尤其是当类的继承层次非常多的时候。它须要一层层的查找其效率将会更低,为了加快查找调用的速度,Objective-c对消息查找做了优化。
	从前一节的类对象我们知道它含有一个?struct objc_cache *cache成员,这个缓存就是为了提高查找的效率的。

每一个类都有自己的缓存,同一时候包含继承的方法和在该类中定义的方法。

当我们在查找IMP 时:

     1.首先去该类的方法 cache 中查找,假设找到了就返回它
     2.假设没有找到,就去该类的方法列表中查找。

假设在该类的方法列表中找到了,则将 IMP 返回,并将它增加cache中缓存起来。依据近期使用原则。这种方法再次调用的可能性非常大。缓存起来能够节省下次调用再次查找的开销。

? ? ?3.假设在该类的方法列表中没找到相应的 IMP,在通过该类结构中的 super_class指针在其父类结构的方法列表中去查找,直到在某个父类的方法列表中找到相应的IMP,返回它。并增加cache中。

? ? ?4.假设在自身以及全部父类的方法列表中都没有找到相应的 IMP,则进入下文中要讲的消息转发流程。


二 消息转发

? ? ? 给一个对象发送它不能处理的消息会得到出错提示,然而。Objective-C执行时系统在抛出错误之前,会给消息接收对象发送一条特别的消息forwardInvocation 来通知该对象,该消息的唯一參数是个NSInvocation类型的对象——该对象封装了原始的消息和消息的參数。

我们能够实现forwardInvocation:方法来对不能处理的消息做一些默认的处理,也能够将消息转发给其他对象来处理,而不抛出错误。

       关于消息转发的作用。能够考虑例如以下情景:假设,我们须要设计一个能够响应negotiate消息的对象,而且能够包含其他类型的对象对消息的响应。 通过在negotiate方法的实现中将negotiate消息转发给其他的对象来非常easy的达到这一目的。

更进一步,假设我们希望我们的对象和另外一个类的对象对negotiate的消息的响应全然一致。

一种可能的方式就是让我们的类继承其他类的方法实现。 然而。有时候这样的方式不可行。由于我们的类和其他类可能须要在不同的继承体系中响应negotiate消息。

尽管我们的类无法继承其他类的negotiate方法,但我们仍然能够提供一个方法实现,这种方法实现仅仅是简单的将negotiate消息转发给其他类的对象,就好像从其他类那儿“借”来的现一样。例如以下所看到的:
- negotiate  {
    if ([someOtherObject respondsToSelector:@selector(negotiate)])
        return  [someOtherObject negotiate];

??? return self;

}

      这样的方式显得有欠灵活。特别是有非常多消息都希望传递给其他对象时,我们就必须为每一种消息提供方法实现。此外,这样的方式不能处理未知的消息。

当我们写下代码时。全部我们须要转发的消息的集合都必须确定。

然而,实际上,这个集合会随着执行时事件的发生,新方法或者新类的定义而变化。

forwardInvocation:消息给这个问题提供了一个更特别的。动态的解决方式:当一个对象由于没有相应的方法实现而无法响应某消息时,执行时系统将通过forwardInvocation:消息通知该对象。

每一个对象都从NSObject类中继承了forwardInvocation:方法。然而,NSObject中的方法实现仅仅是简单地调用了doesNotRecognizeSelector:。

通过实现我们自己的forwardInvocation:方法,我们能够在该方法实现中将消息转发给其他对象。

要转发消息给其他对象,forwardInvocation:方法所必须做的有:
     1.决定将消息转发给谁。而且
     2.将消息和原来的參数一块转发出去
消息能够通过invokeWithTarget:方法来转发:

- (void) forwardInvocation:(NSInvocation *)anInvocation
{
    if ([someOtherObject respondsToSelector:[anInvocation selector]])
        [anInvocation invokeWithTarget:someOtherObject];
    else
        [super forwardInvocation:anInvocation];
}?
? ? ?转发消息后的返回值将返回给原来的消息发送者。您能够将返回不论什么类型的返回值,包含: id。结构体,浮点数等。
       forwardInvocation:方法就像一个不能识别的消息的分发中心。将这些消息转发给不同接收对象。或者它也能够象一个运输站将全部的消息都发送给同一个接收对象。它能够将一个消息翻译成另外一个消息,或者简单的"吃掉“某些消息。因此没有响应也没有错误。forwardInvocation:方法也能够对不同的消息提供相同的响应,这一切都取决于方法的具体实现。

该方法所提供是将不同的对象链接到消息链的能力。

注意:?forwardInvocation:方法仅仅有在消息接收对象中无法正常响应消息时才会被调用。

所以。假设我们希望一个对象将negotiate消息转发给其他对象,则这个对象不能有negotiate方法。

否则。forwardInvocation:将不可能会被调用。

三 小结

? ? ? ? 如今当我们回过头来在看http://blog.csdn.net/zhangzhebjut/article/details/24134863中的实例应该明确了IOS中整个函数的调用流程。IOS的执行时还是挺强大的,通过对执行时的学习。对Objective-c类型系统有一个更加深刻的了解。

? ? ? ? ?事实上假设熟悉Python的人应该也知道,Python是一个全动态的语言,它的类型系统和Objective-c有些相像,有时间给我会将其与Objective-c做一个具体的对比。

原文地址:https://www.cnblogs.com/mqxnongmin/p/10955783.html

时间: 2024-11-12 02:10:13

IOS -执行时 (消息传递再探究)的相关文章

iOS执行时工具-cycript

cycript是大神saurik开发的一个很强大的工具,能够让开发人员在命令行下和应用交互,在执行时查看和改动应用.它确实能够帮助你破解一些应用,但我认为这个工具主要还是用来学习其它应用的设计(主要是UI的设计及实现). 这个工具使用了Objective-C和Javascript的混合模式,能够实时的和应用交互甚至改动应用.它的网址请猛戳这里.在官网上能够下载到完整的软件包.使用的方式有两种,一种是在越狱的设备上通过MobileSubstrate加装,这样能够在全部的应用里使用:还有一种是通过静

iOS AFNetWorking中block执行完后再执行其它操作

需求:同时进行两次网络请求,网络请求是异步的,在网络请求成功后进行其它的操作.两个网络请求是这样,一个网络请求中block执行完之后,再进行其它操作,也是一样的原理,只是这时候不需要线程组了,只需要信号量.当然也适用于所有的block. 接下来就说下,在两次异步请求之后要做的操作. 利用线程组和信号量来完成,看代码 - (void)getData { NSString *appIdKey = @"8781e4ef1c73ff20a180d3d7a42a8c04"; NSString*

Java的初始化块、静态初始化块、构造函数的执行顺序及用途探究

随笔- 40  文章- 0  评论- 1 Java的初始化块.静态初始化块.构造函数的执行顺序及用途探究 Java与C++有一个不同之处在于,Java不但有构造函数,还有一个”初始化块“(Initialization Block)的概念.下面探究一下它的执行顺序与可能的用途. 执行顺序 首先定义A, B, C三个类用作测试,其中B继承了A,C又继承了B,并分别给它们加上静态初始化块.非静态初始化块和构造函数,里面都是一句简单的输出. 主类Main里面也如法炮制. 1 class A { 2 st

iOS系列 基础篇 03 探究应用生命周期

iOS系列 基础篇 03 探究应用生命周期 目录: 1. 非运行状态 - 应用启动场景 2. 点击Home键 - 应用退出场景 3. 挂起重新运行场景 4. 内存清除 - 应用终止场景 5. 结尾 本篇主要探讨的是iOS应用中各种状态的跃迁过程,建议大家通过修改AppDelegate.swift,在每个过程中添加日志输出代码,从而观察其变化. 作为应用程序的委托对象,AppDelegate类在应用程序生命周期的不同阶段会回调不同的方法. 首先,咱们先来了解一下iOS应用的不同状态和他们之间的关系

ios开发时,在Xcode中添加多个targets进行版本控制

  在ios开发时,我们经常会遇到对同一个app开发多个版本(Pro.Lite.Free)的情况,这里就涉及到xcode里通过添加多个targets来进行版本控制的问题了,下面就简单说明一下: 点击左侧的工程名称,右侧会出现PROJECT和TARGETS,点击你现在的target,假如叫A,右键弹出菜单中,选择Duplicate,复制一个相同的target,复制的target一般叫A copy,A copy和A的设置(编译条件.源文件.资源文件)完全一样,此时你可以根据需要修改A copy的编译

功能奇数次执行和偶数次执行时的结果不同的故障复盘

场景:将数据库查询到的数据,写入一个xls文件.完成后,多点几次,发现一个问题,偶数次生成的文件比较小,打开一看,里面只有一行,只有标题,没有内容.分析日志,发现偶数次执行时预期的执行进程中没有相关的日志打印 分析:奇数次执行与预期一致,就不再分析了:偶数次执行时虽没有找到日志,但生成的文件名与文件格式均与预期一致,唯独缺少文件内容.重启进程打开调试,果然在偶数次执行时,Eclipse中相关代码并没有触发,这与预期进程中没有找到相关日志打印是一致的.(事后分析:有与代码逻辑相现的结果文件名及标题

Titanium中调用ios组件时语言不是本地化的解决方法

用Titanium开发的ios应用中,当调用系统组件时,尽管手机已经设置了系统语言为中文,但那些组件的界面却仍为英文.比如调用iphone中的相册组件,其界面为: 那么怎么让它跟系统语言保持一致呢? 在原生的ios开发中,只需要在info.plist中把 CFBundleAllowMixedLocalizations 设置为 true 就行了,代表Localized resources can be mixed,就是允许库使用本地语言资源. 那么在Titanium中该怎么做呢? 其实也很简单,T

给listview加动画,让动画执行结束后再刷新

问题:当给listview的条目加动画时,例如添加一个条目或者移除一个条目,动画效果会和添加删除条目的逻辑同时进行,因为动画并不是阻塞式的,这样会造成动画还没有结束,条目已经添加或者移除,从而动画作用在下面一个条目上面. 解决办法:想办法让动画执行完了之后再进行条目的添加或者移除操作(也就是刷新数据适配器),可以通过设置动画监听来实现,将要添加或者删除条目的逻辑放到动画监听的方法中,可以放到动画结束时调用的方法中,这样就保证了动画先执行,然后才刷新数据适配器 holder.iv_lock.set

iOS执行时与method swizzling

C语言是静态语言,它的工作方式是通过函数调用,这样在编译时我们就已经确定程序怎样执行的.而Objective-C是动态语言,它并不是通过调用类的方法来执行功能,而是给对象发送消息,对象在接收到消息之后会去找匹配的方法来执行.这样的做法就把C语言在编译时的工作挪到了执行时来做,能够获得额外的灵活性. 在Objective-C中有个@selector,在非常多地方被翻译成"选择子".实际上,对于类的实例对象来说,类的方法是用一个数字来代表的,并不是是我们看到的一个长长的带着:这个字符的一串