详解Objective-C消息传递机制

 Objective-C获取消息工作机制是本文要介绍的内容,看name mangling的时候,也讲到了Objective-C的name mangling,于是又重新读了一下Objective-C 2.0 programming Language以及Objective-C 2.0 Runtime Reference里的相关内容,自己归纳一下  。

 

   MyClass.h  
   @interface MyClass : NSObject  
   {  
    }  
   @end  
   MyClass.m  
   #import < span>usr/include/objc/runtime.h> 
   #import “MyClass.h”  
   void myClassIMP(id _rec, SEL _cmd, int theInt)  
   {  
      NSLog(@”dynamic added method:%d”,theInt);  
   }  
   
- (id)init  
{  
    if( ( self = [super init]) != nil )  
    {  
   class_addMethod([MyClass class], @selector(dynGeneratedMethod:),(IMP)myClassIMP,”v@:i”);  
     }  
    return self;  
}  
 
Main.c  
#import “MyClass.h”  
int main(int argc, char *argv[])  
{  
  MyClass theInstance = [[MyClass alloc] init];  
  [theInstance dynGeneratedMethod:10];  
 return 0;  

  这段代码执行的结果是在控制台上输出:


dynamic added method:10 

  接着来详细分析一下上面的代码:

  在ObjC的类中这样的一个声明 – (void)foo:(int)a;被称作方法(method),而在调用的地方: [theClass foo:10];则被称之为发送消息(send message),具体来说是给对象theClass 发送foo:消息,注意这里foo后面的”:”,它也是消息名称的一部分,最前面的-代表实例方法,+代表类方法  。而类似的语句,在C或C++中,通常被称为呼叫函数(call function),在ObjC中,函数(function)一词很少用到,不是它不存在,而是它被ObjC runtime给隐藏了起来  。
   
如前所述,ObjC是以消息机制来工作的,但其实诸如-(void)foo:(int)a的语句在编译时被objc_msgSend(receiver,selector,arg1,arg2,….)替换了,所以其实每一条发送消息的代码本质上还是调用函数(call function),不过他们调用的都是同一个函数objc_msgSend(也可能是objc_msgSend_stret(返回值是结构体),objc_msgSend_fpret(返回值是浮点型)等)

  分析objc_msgSend的参数,第一个receiver的类型是id,代表接受消息的对象,第二个是selector代表接收对象的方法,后面的是该方法的参数,之前那条语句的被编译器替换后就是:


[theClass foo:10]  -> objc_msg(theClass,@selector(foo:),10); 

  因为消息的接受对象和接受对象的方法都参数化,所以在运行时刻,接受对象和接受对象的方法都可以是动态的!

  比如说程序里面可以这样写:


id helper = getTheReceiver();  
SEL request = getTheSelector();  
[helper performSelector:request]; 

  它的实现是基于ObjC runtime. NSObject类实现了这套机制,所以每一个继承于NSObject的类都能自动获得runtime的支持  。在这样的一个类中,有一个isa指针,指向该类定义数据结构体,这个结构体是由编译器编译时为类(须继承于NSObject)创建的.在这个结构体中有包括了指向其父类类定义的指针以及Dispatch table. Dispatch table是一张SEL和IMP的对应表  。

  对于名称相同的方法,他们都有相同的SEL,方法的名称不包括类名称,所以子类和父类中的同名方法拥有相同的SEL,但是他们的实现可以各不相同,因而在他们各自的Dispatch表中SEL所对应的IMP是不同的,IMP是一个函数指针,而虽然每一个SEL对应的是一个方法的名称,但考虑到效率,SEL本身是一个整型,编译器会另外生成一张SEL和方法名称对应的表  。有了这样的结构,objc就可以实现多态了  。还是这行代码:


[theClass foo:10]; 

  是向theClass发送了foo:消息,那么首先在theClass的类结构的Dispatch table里找有没有对应的SEL,如果有的话,就表示theClass有响应该消息的方法,程序就跳到该方法的代码地址头(由IMP指定),开始执行  。如果在theClass的Dispatch table找不到对应的SEL,那么就会通过isa所指的结构体中包含的父类指针,到父类里面去寻找,如果到最后还是没有找到,就会出现runtime error.所以说,即使theClass以及它的父类都没有定义-(void) foo:(int)a方法,程序还是可以通过编译,但如果是用xcode的话,编译器会有警告,告知theClass可能无法响应该消息  。不会报错的原因是类的方法也可以在执行时刻创建!上面的代码:


class_addMethod([MyClass class], @selector(dynGeneratedMethod:),(IMP)myClassIMP,”v@:i”); 

  就是给MyClass类在执行时刻增加了一个响应dynGeneratedMethod:消息的方法,这样之后对任何MyClass的instance类发送dynGeneratedMethod:消息,就会得到响应了.myClassIMP是类收到该消息时要调用的方法,其声明如下:


void myClassIMP(id _rec, SEL _cmd, int theInt) 

  这个方法的前面两个参数是必须的,之后的参数才是我们实际用到的参数,数目和@selector()中的冒号数一样,冒号数代表的就是参数个数  。第一个参数是消息的接受对象,是MyClass的实例,第二个参数是由SEL代表的具体消息  。

  Class_addMethod的最后一个参数是表示dynGeneratedMethod:的返回值和参数信息,不过我自己试了一下,这个参数不起作用  。

  几个要点:

  1、对于C中被称为函数(function)和函数调用(function call)的地方,在ObjC中被叫做方法(method)和发送消息(send message).试图调用未定义的方法会导致编译错误,而发送一条消息,即使没有任何类定义了响应该消息的方法,编译时也不会报错,从语义上讲这也是对的,发一条消息本来就不要求一定有人会响应,不过如果执行到发送消息的代码时真的没有类可以响应的话,是会发生runtime error,为了避免这种事情发生,可以先进行检测,这样写:


if( [myClass respondsToSelector:@selector(foo:)])  
{  
   [myClass foo:10];  

  我感觉ObjC这样的一套sender receiver的定义更注重面向对象的概念  。类是一个接收者(receiver),如果定义了某个方法,就可以接收和这个方法名称相同的消息  。而使用该类的client(sender),则尝试向该类发送消息.如果匹配了,就跳到类的方法里执行  。

  2、方法名称是诸如foo:,不包括返回类型,参数类型,而又因为一个foo:对应于一个SEL,所以说ObjC不支持相同的foo:有不同的返回类型,也不支持重载  。不过类方法和实例方法可以有相同的名字,而又有不同类型的参数和返回类型,因为它们不是处在同一张dispatch table中  。

  3、不仅类的方法可以运行时刻创建,类本身也可以在运行时刻创建,前面提到继承于NSObject的类,编译器会帮忙生成ObjC runtime所需要的类结构定义,只要我们在代码里也按照那个结构创建了自己的类,那一样可以获得ObjC runtime的支持  。 

 

转载于:https://www.cnblogs.com/DamonTang/archive/2012/11/08/2760580.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/474869.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

vuedraggable示例_vuedraggable快速入门

value和list这两个都可以给一个draggable注入数据源&#xff1a;/*value注入数据源*/export default {data() {return {list:[{name:aaa,id:1,},{name:bbb,id:2,}]};}}/*list注入数据源头*/export default {data() {return {list:[{name:aaa,id:1,},{name:bbb,id:2,}]};}}它们的…

LeetCode MySQL 1468. 计算税后工资

文章目录1. 题目2. 解题1. 题目 Salaries 表&#xff1a; ------------------------ | Column Name | Type | ------------------------ | company_id | int | | employee_id | int | | employee_name | varchar | | salary | int | ----------…

ETL详解

ETL是将业务系统的数据经过抽取、清洗转换之后加载到数据仓库的过程&#xff0c;目的是将企业中的分散、零乱、标准不统一的数据整合到一起&#xff0c;为企业的决策提供分析依据。 ETL是BI项目重要的一个环节。 通常情况下&#xff0c;在BI项目中ETL会花掉整个项目至少1/3的时…

博客园

为更好的分享本人的博客&#xff0c;本人在博客园地址&#xff1a;http://www.cnblogs.com/blogyuan/&#xff0c;里面也有博文&#xff0c;本博客将不定期将两个博客上的博文同步&#xff0c;欢迎转载&#xff01; 转载于:https://www.cnblogs.com/blogyuan/archive/2012/11/0…

LeetCode MySQL 1421. 净现值查询

文章目录1. 题目2. 解题1. 题目 表: NPV ------------------------ | Column Name | Type | ------------------------ | id | int | | year | int | | npv | int | ------------------------ (id, year) 是该表主键. 该表有…

apache大师+伪静态_Apache开启伪静态示例

Apache开启伪静态示例环境&#xff1a;系统 WindowsApache 2.2加载Rewrite模块&#xff1a;在conf目录下httpd.conf中找到LoadModule rewrite_module modules/mod_rewrite.so这句&#xff0c;去掉前边的注释符号“#”&#xff0c;或添加这句。允许在任何目录中使用“.htaccess”…

从入门到深入Fiddler 2 (二)

AutoResponder 快速引用 今天我们来看一下Fiddler的AutoResponder选项卡的强大功能。 Fiddler的AutoResponder 选项卡允许你使用本地硬盘的文件来作为返回内容&#xff0c;而不是把请求发往服务器。 创建AutoResponder 规则。 使用AutoResponder选项卡&#xff0c;你可以创建一…

探索性数据分析EDA及数据分析图表的选择

文章目录一、探索性数据分析EDA二、数据分析图表的选择一、探索性数据分析EDA 探索性数据分析(Exploratory Data Analysis&#xff0c;简称EDA)&#xff0c;指对数据分析的过程中尽量不加入先验假设&#xff0c;而是通过作图表和统计等方式来探索数据结构和规律。 在EDA中你可…

LeetCode MySQL 608. 树节点

文章目录1. 题目2. 解题1. 题目 给定一个表 tree&#xff0c;id 是树节点的编号&#xff0c; p_id 是它父节点的 id 。 ---------- | id | p_id | ---------- | 1 | null | | 2 | 1 | | 3 | 1 | | 4 | 2 | | 5 | 2 | ---------- 树中每个节点属于以下三种类…

xshell 打开文件跳转到最后_如何在Xshell中打开Xftp

Xftp作为远程文件传输软件&#xff0c;与Xshell终端模拟器同属于Xmanager&#xff0c;这两款软件经常配合使用&#xff0c;用来给远程服务器上传文件非常方便。那么在使用Xshell的时候怎样打开Xftp呢&#xff1f;下面就来给大家介绍具体操作技巧。如果安装了一、XShell中打开如…

网络资源-深入剖析Binding2(学习)

WPF Binding WPF里分三种Binding&#xff1a;Binding&#xff0c; PriorityBinding, MultiBinding,这三种Binding的基类都是BindingBase&#xff0c;而BindingBase又继承于MarkupExtension Binding&#xff1a; 提供对绑定定义的高级别访问&#xff0c;绑定将绑定目标对象&…

LeetCode MySQL 1045. 买下所有产品的客户

文章目录1. 题目2. 解题1. 题目 Customer 表&#xff1a; ---------------------- | Column Name | Type | ---------------------- | customer_id | int | | product_key | int | ---------------------- product_key 是 Product 表的外键。Product 表&#xff1…

统计学中常用的数据分析方法汇总

文章目录一、描述统计二、假设检验三、信服分析四、列联表分析五、相关分析六、方差分析一、描述统计 描述统计是通过图表或数学方法&#xff0c;对数据资料进行整理、分析&#xff0c;并对数据的分布状态、数字特征和随机变量之间关系进行估计和描述的方法。描述统计分为集中…

java组装树状结构数据集合_JAVA构建List集合为树形结构

package com.zving.tree;import java.util.ArrayList;import java.util.List;/*** 树形结构实体类* author clove*/public class Node {private int id;private int pid;private String name;private String type;private List children new ArrayList<>();public Node(…

过拟合(overfitting)和欠拟合(underfitting)出现原因及如何避免方案

文章目录欠拟合一、什么是欠拟合&#xff1f;二、欠拟合出现原因三、解决欠拟合(高偏差)的方法过拟合一、什么是过拟合&#xff1f;二、过拟合出现原因三、解决过拟合(高方差)的方法欠拟合 一、什么是欠拟合&#xff1f; 欠拟合是指模型不能在训练集上获得足够低的误差。换句…

斯坦佛编程教程-Unix编程工具(五)

Unix Shell 这个部分是总结一下在Unix shell中使用的大多数的命令。 文件命令 cd directory 改变路径&#xff0c;如果directory没有指明&#xff0c;那么就返回home目录。 pwd 显示当前路径。 ls 显示目录下的内容&#xff0c;ls -a会显示以点开头的文件&#xff0c;ls…

LeetCode MySQL 612. 平面上的最近距离

文章目录1. 题目2. 解题1. 题目 表 point_2d 保存了所有点&#xff08;多于 2 个点&#xff09;的坐标 (x,y) &#xff0c;这些点在平面上两两不重合。 写一个查询语句找到两点之间的最近距离&#xff0c;保留 2 位小数。 | x | y | |----|----| | -1 | -1 | | 0 | 0 | …

数据库开发设计规范及表结构设计原则

文章目录一. 命名规范二. 库表基础规范三. 字段规范四. 索引规范五. SQL设计①.正规化表设计原则②.SQL设计③.“三少原则”六. 行为规范一. 命名规范 1.库名、表名、字段名必须使用小写字母&#xff0c;并采用下划线分割 (1)MySQL有配置参数lower_case_table_names1&#xff…

python离散点的线性回归_用python玩点有趣的数据分析——一元线性回归分析实例...

http://python.jobbole.com/81215/本文参考了博乐在线的这篇文章&#xff0c;在其基础上加了一些自己的理解。其原文是一篇英文的博客&#xff0c;讲的十分通俗易懂。本文通过一个简单的例子&#xff1a;预测房价&#xff0c;来探讨怎么用python做一元线性回归分析。1. 预测一下…

[BTS] WCF-SAP Connect to SAP gateway failed

日志名称: Application来源: BizTalk Server日期: 2012/11/15 15:58:24事件 ID: 5743任务类别: (1)级别: 警告关键字: 经典用户: 暂缺计算机: AppTestSrv.AppCenter.xz描述:The ad…