刚开始使用Objective-C时,总是习惯将对象间发送消息之间称呼为方法调用。心想,这和c#不是一回事吗?不就是调用实例方法吗,还搞个消息发送作甚,最后还不是要转化为方法的调用?通过一段时间的理解学习,觉得并不是消息调用那么简单,才体会到了水果公司的强大。

  在现实生活中,每一个人包括工具都是一个独立的个体,假如张三是顾客(Customer),李四是某一个店的普通的技工(Artisan),当张三需要李四做一个特殊的板凳时,一般情况下,张三会直接跟李四说:喂,四哥,给我做一个某某功能的板凳吧!

  好了,回到正题,不知道看到上面这段场景,然后联想一下我们程序的世界,有没有什么想法?不管大家有没有,反正我是有的。下面将我个人的想法简单描述一下:我们可以将张三和李四都看作是程序中的对象,李四作板凳看做时李四对象上的一个实例方法。先说说C#中是什么样子,代码如下:

private void test()//Customer中的一个方法
{
Artisan lisi = new Artisan();
lisi.MakeBanDeng();
}

  表面看起来很合乎情理,其实从现实的角度讲并不合理。为什么这么说呢?李四的确是有个技能是makeBanDeng,通过上述直接调用的方式展示的是张三让一定要让李四亲自做板凳。可是有时候事情并不是这样。会有如下几种情况:

1、李四不会做板凳,张三只是个人认为李四会做板凳,多么的一厢情愿啊。

2、李四不会做板凳,虽然张三让李四做板凳,但是李四认识很多人,知道有的人会做,可以交给其它人做,最后给张三,仿佛是自己做的一样。

3、李四不会做板凳,也不认识会做板凳的人,板凳店经理知道了这个事,决定亲自处理它。

4、李四会做板凳,这没有什么好说的,那就直接做得了。

  既然有四种情况,那么怎么能简单的通过直接让李四做板凳来完成呢?而类似C#这样的通过方法调用无一不是实现的第四种,前面三种都不予考虑,仿佛张三让李四做板凳就一定是李四做似的。

  接下来我们看看在Objective-c中是如何实现的呢?Objective-c把方法的调用以消息通信代替,张三让李四做板凳这件事在Objective-c中是通过张三告诉李四做一个板凳这个消息来完成,这是不是仿佛更符合现实情况?这就是Objective-C中的消息传递,就像对象间交流一样,更自然更容易让人接受。

  既然我们知道了Objective-C中对象间是采用消息传递来完成互动,那么它们内部有什么机制呢?下面我来一一揭开。

  还是用上面的例子来说明,当上面的第一种情况出现的时候,也就是当 Objective-C中的对象收到了一个没有相应的方法来响应的消息的时候,OC会通知该对象解析出实例方法,这个消息对应的selector名称为resolveInstanceMethod:。我们称之为动态方法解析:

一、动态方法解析相关代码及说明

  下面是Person的头文件Person.h,里面只有一个方法makeMaterial。

//
// Person.h #import <Foundation/Foundation.h> @class Material; @interface Person : NSObject - (Material *)makeMaterial; @end

下面是主程序调用代码,告知李四做板凳。

#import <Foundation/Foundation.h>
#import "Material.h"
#import "Person.h" int main(int argc, const char * argv[]){
Person *lisi = [[Person alloc] init];
Material *bandeng = [lisi performSelector:@selector(makeBanDeng)];
NSLog(@"%@",bandeng);
}

  我们知道在李四所属类Person.h中是没有makeBanDeng这个方法的,默认情况下一定会找不到该方法,下面是利用resolveInstanceMethod:来处理该问题:

//
// Person.m
// Created by 084221019 on 15/9/19.
// Copyright © 2015年 forwk1990. All rights reserved.
// #import "Person.h"
#import "Material.h"
@import ObjectiveC; @implementation Person id makeBanDeng(id self,SEL _cmd){
return [[Material alloc] initWithDesc:@"li si make a bandeng"];
}; - (Material *)makeMaterial{
Material *material = [[Material alloc] initWithDesc:@"green"];
return material;
} + (BOOL)resolveInstanceMethod:(SEL)sel{
NSString *selectorString = NSStringFromSelector(sel);
if([selectorString compare:@"makeBanDeng"] == NSOrderedSame){
class_addMethod(self,sel,(IMP)makeBanDeng,"@@:");
return YES;
}else{
return [super resolveInstanceMethod:sel];
}
} @end

  我们看到在resolveInstanceMethod中,为该对象类新增了一个实例方法叫makeBanDeng。这就好比,张三叫李四做,但是李四不会做,这个时候李四选择自我学习做板凳,学成后(return YES),照样可以做板凳。下面是程序的运行结果:

  如果动态方法解析中并没有解析到合适的方法,也就是之前描述的第二种情况,李四决定找其它人来做。这个在Objective-C中叫消息的备援接收。selector名称为forwardingTargetForSelector:

二、消息的备援接受者的相关代码及说明

  新增对象类Artisan,头文件Artisan.h及Artisan.m代码如下:

#import <Foundation/Foundation.h>
@class Material; @interface Artisan : NSObject - (Material *)makeBanDeng; @end #import "Artisan.h"
#import "Material.h" @implementation Artisan - (Material *)makeBanDeng{
return [[Material alloc] initWithDesc:@"Artisan make a bandeng"];
} @end

 Person.m代码更改如下:

#import "Person.h"
#import "Material.h"
#import "Artisan.h"
@import ObjectiveC; @implementation Person
- (Material *)makeMaterial{
Material *material = [[Material alloc] initWithDesc:@"green"];
return material;
}
- (id)forwardingTargetForSelector:(SEL)aSelector{
NSString *selectorString = NSStringFromSelector(aSelector);
if([selectorString compare:@"makeBanDeng"] == NSOrderedSame){
return [[Artisan alloc] init];
}
else{
return nil;
}
} @end

  运行结果如下:

  我们可以发现通过forwardingTargetForSelector:可以重新定位消息接受者,这相当于李四重新找了一个认识的人来做这个事情。这也就是第二种情况。

  如果李四自己不会做同时又找不到会做的人呢?在现实生活中,店长为了不丢掉这个生意,可能会召集全员开会商讨一下如何做这个板凳。在Objective-C中,对应的会启用消息转发机制,收集所有消息的信息然后创建NSInvocation消息对象来处理这件事。NSInvocation包括了selector、target以及参数信息。这个selector名称就是forwardInvation:,这个过程系统成为消息转发。我们有了NSInvocation对象就可以修改这个消息的一切信息,可以修改Target指定某一个人来做,修改selector用其它方法来相应。当然不建议在这里修改Target,修改 Target在上面那种情况修改就可以了,没有必要等到这一步再做。

三、完整的消息转发代码及说明

  现在修改Person.m代码如下:

#import "Person.h"
#import "Material.h"
#import "Artisan.h"
@import ObjectiveC; @implementation Person - (Material *)makeMaterial{
Material *material = [[Material alloc] initWithDesc:@"green"];
return material;
} - (void)forwardInvocation:(NSInvocation *)anInvocation{
NSString *selectorString = NSStringFromSelector(anInvocation.selector);
if([selectorString compare:@"makeBanDeng"] == NSOrderedSame){
[anInvocation setTarget:[[Artisan alloc] init]];
}else{
//...什么都不做就会引发异常
}
}

  修改成如上代码,产生的结果和上一个结果一样。如果到这一步还是没有处理该消息,那么系统将调用doesnotRecognizeSelector方法来抛出异常。到了这里我们是不是想到如果上一步都还没有处理,其实我们还是可以通过重写这个方法来继续处理该消息。

  总结起来就是:

1、李四收到做板凳的消息,发现自己不会做,系统问李四是否需要添加这份技能(resolveInstanceMethod中addMethod),

2、李四自己学不会,但是李四决定找一个认识的人来处理这件事(forwardTargetForSelector:)

3、李四实在是找不到任何人来处理这件事,店长或经理搜集客户需求(selector,methodArgument,处理人target),启用板凳店的终极处理NSInvocation

4、启用之后还是没有什么卵用,店长无赖的告诉张三,doesnotRecognizeSelector

  下面为整个系统流程图:

Objective-c中的对象间的消息传递以及消息路由的更多相关文章

  1. [Swift实际操作]八、实用进阶-(5)通过间接代理进行对象间的消息传递

    本文将为你演示,如何通过简介代理的方式,进行对象间的消息传递在左侧的项目导航区,打开自定义视图的代码文件(CustomView.swift). import UIKit class CustomVie ...

  2. [Swift实际操作]八、实用进阶-(6)通过属性进行对象间的消息传递

    本文将演示,如何通过一个对象作为另一个对象的属性,从而通过设置属性的值,来实现对象之间的消息传递.首先打开自定义视图的代码文件(CustomView.swift) import UIKit //使当前 ...

  3. C#中实现对象间的更新操作

    最近工作的时候遇到一个问题,根据Web端接收到的对象obj1,更新对应的对象值ogj2.先判断obj1中属性值是否为null, 若不等于null,则更新obj2中对应属性值:若等于null,则保持ob ...

  4. [Swift实际操作]八、实用进阶-(7)使用通知的方法进行对象间的消息传递

    本文将为你演示,如何使用Notification Center进行消息的传递.通知中心是一个调度消息通知的类,采用单例设计模式,实现数据传递,回调等功能.首先打开自定义视图的代码文件(CustomVi ...

  5. InheritableThreadLocal 在线程池中进行父子线程间消息传递出现消息丢失的解析

    在日常研发过程中,我们经常面临着需要在线程内,线程间进行消息传递,比如在修改一些开源组件源码的过程中,需要将外部参数透传到内部,如果进行方法参数重载,则涉及到的改动量过大,这样,我们可以依赖Threa ...

  6. NSNotification,NSNotificationCenter的使用、iOS中五种对象间传值的方式

    学习内容 NSNitification与NotificationCenter(通知与通知中心) 通知的使用 [[NSNotificationCenter defaultCenter]addObserv ...

  7. C#中??和?分别是什么意思? 在ASP.NET开发中一些单词的标准缩写 C#SESSION丢失问题的解决办法 在C#中INTERFACE与ABSTRACT CLASS的区别 SQL命令语句小技巧 JQUERY判断CHECKBOX是否选中三种方法 JS中!=、==、!==、===的用法和区别 在对象比较中,对象相等和对象一致分别指的是什么?

    C#中??和?分别是什么意思? 在C#中??和?分别是什么意思? 1. 可空类型修饰符(?):引用类型可以使用空引用表示一个不存在的值,而值类型通常不能表示为空.例如:string str=null; ...

  8. C++对象间通信组件,让C++对象“无障碍交流”

    介绍 这是很久之前的一个项目了,最近刚好有些时间,就来总结一下吧! 推荐初步熟悉项目后阅读本文: https://gitee.com/smalldyy/easy-msg-cpp 从何而来 这要从我从事 ...

  9. 使用AutoMapper进行对象间映射

    在开发过程中,难免遇到下面这种情况:两个(或多个)对象所拥有的大多数属性是重复的,我们需要在对象间进行映射(即将一个对象的属性值赋给另一个对象.通常我们可以进行如下操作: A a=new A(); a ...

随机推荐

  1. no datanode to stop

    昨晚整了半天,遇上的问题是通过start-all.sh无法启动datanode,然后关闭时就会报no datanode to stop ,引起这个的原因是因为我多次格式化,导致namespaceID不 ...

  2. C# WebService的简单和复杂参数类型和结果的JSON格式

    Jquery作为一款优秀的JS框架,简单易用的特性就不必说了.在实际的开发过程中,使用JQ的AJAX函数调用WebService 的接口实现AJAX的功能也成了一种比较普遍的技术手段了.WebServ ...

  3. 使用synchronize同步关键字来同步代码快

    1.synchronized关键字的作用域有二种: 1)是某个对象实例内,synchronized aMethod(){}可以防止多个线程同时访问这个对象的synchronized方法(如果一个对象有 ...

  4. Java模拟登陆02【转载】

    在使用java访问URL时,如果该URL需要身份验证,那么就不能够直接访问,因为没有登陆.那么,如何解决这个问题呢?     方法是使用java模拟登陆,登陆后记录下cookie信息,在下次发起请求时 ...

  5. -_-#【JS】defer / async

    引用JavaScript文件时的两个属性defer和async <script src="js1.js" defer></script> <scrip ...

  6. Metadata Lock原理1

    https://www.percona.com/blog/2013/02/01/implications-of-metadata-locking-changes-in-mysql-5-5/ impli ...

  7. 纯windows下制作变色龙引导安装U盘教程

    原创教程:纯windows下制作变色龙引导安装U盘教程 支持Mavericks和Yosemite 支持白苹果 目标:windows下制作带 Chamelon变色龙引导的黑苹果安装U盘,支持PC机引导安 ...

  8. jQuery Mobile 连接外部连接或切换动画

    jQuery Mobile不同网页之间的跳转问题 jQuery Mobile,一个新的手机终端脚本开发库,从名字可以看出,它是基于jQuery:目前支持很多种手机设备,包括IOS/Android/Bl ...

  9. javascript 通过面向对象编写圆形数字时钟

    效果如图所示,代码如下 <!DOCTYPE html> <html lang="en"> <head> <meta charset=&qu ...

  10. oracle数据字典和动态性能视图

    数据字典和动态性能视图数据字典是oracle数据库中重要的组成部分,提高了数据库的一些系统信息.(静态信息)动态性能视图记载了例程启动后的信息.(动态信息) 数据字典记录了数据的系统信息,是只读表和动 ...