Objective-c中的对象间的消息传递以及消息路由
刚开始使用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中的对象间的消息传递以及消息路由的更多相关文章
- [Swift实际操作]八、实用进阶-(5)通过间接代理进行对象间的消息传递
本文将为你演示,如何通过简介代理的方式,进行对象间的消息传递在左侧的项目导航区,打开自定义视图的代码文件(CustomView.swift). import UIKit class CustomVie ...
- [Swift实际操作]八、实用进阶-(6)通过属性进行对象间的消息传递
本文将演示,如何通过一个对象作为另一个对象的属性,从而通过设置属性的值,来实现对象之间的消息传递.首先打开自定义视图的代码文件(CustomView.swift) import UIKit //使当前 ...
- C#中实现对象间的更新操作
最近工作的时候遇到一个问题,根据Web端接收到的对象obj1,更新对应的对象值ogj2.先判断obj1中属性值是否为null, 若不等于null,则更新obj2中对应属性值:若等于null,则保持ob ...
- [Swift实际操作]八、实用进阶-(7)使用通知的方法进行对象间的消息传递
本文将为你演示,如何使用Notification Center进行消息的传递.通知中心是一个调度消息通知的类,采用单例设计模式,实现数据传递,回调等功能.首先打开自定义视图的代码文件(CustomVi ...
- InheritableThreadLocal 在线程池中进行父子线程间消息传递出现消息丢失的解析
在日常研发过程中,我们经常面临着需要在线程内,线程间进行消息传递,比如在修改一些开源组件源码的过程中,需要将外部参数透传到内部,如果进行方法参数重载,则涉及到的改动量过大,这样,我们可以依赖Threa ...
- NSNotification,NSNotificationCenter的使用、iOS中五种对象间传值的方式
学习内容 NSNitification与NotificationCenter(通知与通知中心) 通知的使用 [[NSNotificationCenter defaultCenter]addObserv ...
- C#中??和?分别是什么意思? 在ASP.NET开发中一些单词的标准缩写 C#SESSION丢失问题的解决办法 在C#中INTERFACE与ABSTRACT CLASS的区别 SQL命令语句小技巧 JQUERY判断CHECKBOX是否选中三种方法 JS中!=、==、!==、===的用法和区别 在对象比较中,对象相等和对象一致分别指的是什么?
C#中??和?分别是什么意思? 在C#中??和?分别是什么意思? 1. 可空类型修饰符(?):引用类型可以使用空引用表示一个不存在的值,而值类型通常不能表示为空.例如:string str=null; ...
- C++对象间通信组件,让C++对象“无障碍交流”
介绍 这是很久之前的一个项目了,最近刚好有些时间,就来总结一下吧! 推荐初步熟悉项目后阅读本文: https://gitee.com/smalldyy/easy-msg-cpp 从何而来 这要从我从事 ...
- 使用AutoMapper进行对象间映射
在开发过程中,难免遇到下面这种情况:两个(或多个)对象所拥有的大多数属性是重复的,我们需要在对象间进行映射(即将一个对象的属性值赋给另一个对象.通常我们可以进行如下操作: A a=new A(); a ...
随机推荐
- 使用Filter防止浏览器缓存页面或请求结果
仅仅须要两步: 1.定义一个Filter: /** * 防止浏览器缓存页面或请求结果 * @author XuJijun * */ public class NoCacheFilter impleme ...
- VS DLL 复制本地
1.引用一个DLL,需要指定路径,复制本地的意思是 把这个DLL复制到exe的Debug目录(调试的时候). 2.复制到本地的动作是在生成的时候执行的,清理的时候会删除. 3.从外部引用一个DLL,不 ...
- sed命令查找<media/msm_cam_sensor.h>替换为"len_msm_cam_sensor.h"
sed -i 's:<media/msm_cam_sensor.h>:"len_msm_cam_sensor.h":g' $(find . -name "*. ...
- javaio学习笔记-字符流类(1)
1.java.io包中的字符流类-BufferedReader和BufferedWriter: BufferedReader:缓存的输入字符流; BufferedWriter:缓存的输出字符流; In ...
- Error creating bean with name 'menuController': Injection of autowired dependency……
出现了一大串错误 Error creating bean with name 'userController': Injection of autowired dependencies failed. ...
- AttributeBehavior
当事件触发的时候... 会循环所有behavior的events 为ower添加事件.....AttributeBehavior 为事件添加如下方法 public function evaluat ...
- Linux守护进程(init.d和xinetd)
http://www.cnblogs.com/itech/archive/2010/12/27/1914846.html
- SVN是什么,svn的目录结构
Svn是一个离线的代码管理,可以多个人一起修改,然后再将修改的内容提交到Svn中.每一个svn服务器中的数据存储单位叫做存储,但是你不仅仅可以把整个存储当作你维护的内容,也可以将其中的某个分支目录像根 ...
- 使用coreData
1.设计数据模型 2.创建持久化视图和控制器 #import "BIDViewController.h" #import "BIDAppDelegate.h" ...
- DataBase 之 数据库中的系统表
名称 地址 说明 sysaltfiles ...