C++模拟实现Objective-C动态类型(附源码)
在OC(Objective-C)里面有动态类型分为以下几类:
-(BOOL)isKindOfClass:classObj 是否是classObj类或其子类 -(BOOL)isMemberOfClass:classObj 是否是classObj的实例 -(BOOL)respondsTosSelector:selector 类中是否有这个方法 NSClassFromString(NSString*); 由字符串得到类对象 NSStringFromClass([类名 Class]); 由类名得到字符串 Class rectClass= [Rectangle class]; 通过类名得到类对象 Class aClass =[anObject class]; 通过实例得到类对象 if([obj1 class]== [obj2 class]) 判断是不是相同类的实例
虽然C++本身不包含以上这些功能,但是相比之下C++是更为底层的语言,其实以上大部分也可以用C++模拟实现。虽然这些对实际开发意义或许不大,但是从中我们也可以了解很多高级语言底层知识,从而更深入了解语言本质或者是语言的灵活性。当然以上接口我在模拟实现时或许会和OC实际接口有差异,但是这并不影响,因为有些只需要做相应修改即可,而我更多的是介绍实现的思想。
首先我们来实现isKindOfClass这个函数,该函数在C++主要是判断一个对象是否从一个类的对象或者是某个类的子类对象。我们模拟实现这个功能,假如说A-B-C-D-E一连串的继承,如果满足以下两个条件:1.每个类都有自己的标识,用于通过标识判断对象是否是这个类的对象;2.对于A-B-C-D-E一连串的继承,作出一张链表,用于查询使用。那么如果我们需要判断C c对象是否是A的子类对象时,那么只需要调用一个查询接口,传入A对象标识,当判断是A类的对象时,再查询B对象,然后查询到C对象,此时判断通过于是返回为真。
//在这里我们用类名字作为唯一标识,也就是szClassName变量
//然后向前向后两个链表用于串起整个继承关系
struct ClassRunTimeType
{
char *szClassName;
static ClassRunTimeType* pFirstClass;
ClassRunTimeType *m_pPrevClassRunTimeType;
ClassRunTimeType *m_pNextClassRunTimeType;
}; //这个类并没有其他用途,唯独借用定义对象时需要执行构造
//通过执行构造函数来初始化我们需要第一时间执行的代码
//每个类都会用他定义一个对应的对象
class CRunTimeType
{
public:
CRunTimeType(ClassRunTimeType *pType);
};
有了以上的基本数据结构接下来可以在实际类中动手脚了
class NSObject
{
public:
virtual void ShowHello(){} BOOL isKindOfClass(ClassRunTimeType *pRunTimeType);
BOOL isMemberOfClass(void *pFun);
void *GetInstallFromName(char *szName); virtual char* GetCurrentClassName()
{
return NSObject::GetClassName();
} static char* GetClassName()
{
return szClassNSObject;
}
}; static char szClassNSObject[] = "NSObject";
ClassRunTimeType NSObject::classNSObject = {szClassNSObject,NULL,NULL};
szClassName[] = "NSObject"就是这个类的唯一标识,如果要判断是否是出自于这个类,只需要对比这个字符串就可以了,相应的调用方法也很简单,也就是BOOL isKindOfClass(ClassRunTimeType *pRunTimeType)的内部实现。
BOOL isKindOfClass(ClassRunTimeType *pRunTimeType)
{
if ( == strcmp(GetCurrentClassName(),pRunTimeType->szClassName))
return TRUE;
return FALSE;
}
当然了,这个时候还仅仅只是实现了判断一个对象是不是某个类的对象,对于是不是子类还是不行,因为没有借助上面的链表查询功能。但是相信到了这个具体怎么实现已经不是难事了,如果有疑问可以调本文对应的源码,里面有详细的说明。
可惜的是respondsTosSelector并没有找到很好的方法,首先Object-C每个成员函数都是虚函数,相对很容易实现一些,而C++里面的非虚函数则完全无法判断。其次即使是虚函数,判断起来也存在一定的困难,以VS2010为例,在禁用优化的情况下,调用一个虚函数会执行以下几步:
ShowHelloEx.ShowHello();
lea ecx,[ShowHelloEx]
call NSObject::ShowHello (191010h)
首先保存this指针的地址到ecx,这也是VS系列管用手法。其次调用的ShewHello函数内部,但是这里并没有调到虚表里面的对应的地址,而是调到这个地方
NSObject::`vcall'{0}':
mov eax,dword ptr [ecx]
jmp dword ptr [eax]
此时再取出虚表地址,然后计算偏移jmp,这时候才是真正虚表的地址,而从函数名获取到的地址只是这里的中转跳转位置也就是191140这个地方。所以说单纯的比较虚表地址来实现这个功能也并不靠谱,换成其他编译器也会存在兼容性问题,所以说这个接口我只能表示无能为力。至于OC为什么能做到,我在想毕竟接口是苹果实现的,而OC编译器也是苹果自己的,苹果可以设定成编译成指定的调用规定来适应接口,而C++就不一样。
NSClassFromString这个函数相对简单,其实很多地方都会用到类似的功能。比如说我们写个程序,在程序上画了一条线,然后保存到文件。如果我们以后要打开这个文件则必须要保存这条线的类名字以及其他特征,读取的时候首先根据类名创建对象,也就是这个函数类似的功能。事实上我们只需要在上面那个结构体中动动手脚就好了,具体实现:
typedef void* ( *pFn)();
//在这里我们用类名字作为唯一标识,也就是szClassName变量
//然后向前向后两个链表用于串起整个继承关系
struct ClassRunTimeType
{
char *szClassName;
static ClassRunTimeType* pFirstClass;
ClassRunTimeType *m_pPrevClassRunTimeType;
ClassRunTimeType *m_pNextClassRunTimeType;
pFn pfn;
};
pfn这个函数主要用于创建对象,我们只需要在给这个结构体赋值的时候,传入一个函数指针,而函数的目的就是创建对象。首先我们需要遍历我们的链表,寻找到对应的类名,然后调用这个函数创建对象即可。相反,NSStringFromClass也可以用类似的方法实现,具体的可以参考示例代码。
至于判断是否同一个实例就很简单了,首先比较地址对不对,对的话就完全一样^_^其次比较唯一标识,符合的话就是同一个类的对象啦。
源码下载: DynamicType.zip
C++模拟实现Objective-C动态类型(附源码)的更多相关文章
- 闪电动画模拟(Dielectric Breakdown Model)附源码
当两个物体之间存在较大的电势差时会出现放电现象,比如生活中常见的闪电现象,闪电形成的条件就是云层积累了大量负电荷之后与地面之间形成了强大的电势差.目前关于闪电建模的方法比较少,下面介绍一种利用电介击穿 ...
- 自然饱和度(Vibrance)算法的模拟实现及其SSE优化(附源码,可作为SSE图像入门,Vibrance算法也可用于简单的肤色调整)。
Vibrance这个单词搜索翻译一般振动,抖动或者是响亮.活力,但是官方的词汇里还从来未出现过自然饱和度这个词,也不知道当时的Adobe中文翻译人员怎么会这样处理.但是我们看看PS对这个功能的解释: ...
- SSE图像算法优化系列八:自然饱和度(Vibrance)算法的模拟实现及其SSE优化(附源码,可作为SSE图像入门,Vibrance算法也可用于简单的肤色调整)。
Vibrance这个单词搜索翻译一般振动,抖动或者是响亮.活力,但是官方的词汇里还从来未出现过自然饱和度这个词,也不知道当时的Adobe中文翻译人员怎么会这样处理.但是我们看看PS对这个功能的解释: ...
- wpf 模拟抖音很火的罗盘时钟,附源码,下载就能跑
wpf 模拟抖音很火的罗盘时钟,附源码 前端时间突然发现,抖音火了个壁纸,就是黑底蕾丝~~~ 错错错,黑底白字的罗盘时钟! 作为程序员的我,也觉得很新颖,所以想空了研究下,这不,空下来了就用wpf, ...
- wpf 模拟3D效果(和手机浏览图片效果相似)(附源码)
原文 wpf 模拟3D效果(和手机浏览图片效果相似)(附源码) pf的3D是一个很有意思的东西,类似于ps的效果,类似于电影动画的效果,因为动画的效果,(对于3D基础的摄像机,光源,之类不介绍,对于依 ...
- ArcGIS紧凑型切片读取与应用2-webgis动态加载紧凑型切片(附源码)
1.前言 上篇主要讲了一下紧凑型切片的的解析逻辑,这一篇主要讲一下使用openlayers动态加载紧凑型切片的web地图服务. 2.代码实现 上篇已经可以通过切片的x.y.z得对应的切片图片,现在使用 ...
- Cesium专栏-雷达遮罩动态扫描(附源码下载)
Cesium 是一款面向三维地球和地图的,世界级的JavaScript开源产品.它提供了基于JavaScript语言的开发包,方便用户快速搭建一款零插件的虚拟地球Web应用,并在性能,精度,渲染质量以 ...
- MVC系列——MVC源码学习:打造自己的MVC框架(二:附源码)
前言:上篇介绍了下 MVC5 的核心原理,整篇文章比较偏理论,所以相对比较枯燥.今天就来根据上篇的理论一步一步进行实践,通过自己写的一个简易MVC框架逐步理解,相信通过这一篇的实践,你会对MVC有一个 ...
- 8个前沿的 HTML5 & CSS3 效果【附源码下载】
作为一个前沿的 Web 开发者,对于 HTML5 和 CSS3 技术或多或少都有掌握.前几年这些新技术刚萌芽的时候,开发者们已经使用它们来小试牛刀了,如今这些先进技术已经遍地开发,特别是在移动端大显身 ...
- 【转】.NET(C#):浅谈程序集清单资源和RESX资源 关于单元测试的思考--Asp.Net Core单元测试最佳实践 封装自己的dapper lambda扩展-设计篇 编写自己的dapper lambda扩展-使用篇 正确理解CAP定理 Quartz.NET的使用(附源码) 整理自己的.net工具库 GC的前世与今生 Visual Studio Package 插件开发之自动生
[转].NET(C#):浅谈程序集清单资源和RESX资源 目录 程序集清单资源 RESX资源文件 使用ResourceReader和ResourceSet解析二进制资源文件 使用ResourceM ...
随机推荐
- express 创建项目
express 创建项目: 1.熟悉express命令 2.创建模板 3.安装组件
- Java 中的 SPI 机制是什么鬼?高级 Java 必须掌握!
作者:sigangjun blog.csdn.net/sigangjun/article/details/79071850 SPI的全名为:Service Provider Interface,大多数 ...
- sshpass批量分发ssh秘钥
首先安装sshpass: yum -y install sshpass 单条命令: sshpass -p“password” ssh-copy-id -i /root/.ssh/id_rsa.pub ...
- [JSOI2019]精准预测
题目 这么明显的限制条件显然是\(\text{2-sat}\) 考虑按照时间拆点,\((0/1,x,t)\)表示\(x\)个人在时间\(t\)是生/死 有一些显然的连边 \[(0,x,t+1)-> ...
- mycat-zookeepr--mycatweb
##############################mycat镜像############################## 5-1 创mycat镜像 wget http://dl.myca ...
- 23种常用设计模式的UML类图
23种常用设计模式的UML类图 本文UML类图参考<Head First 设计模式>(源码)与<设计模式:可复用面向对象软件的基础>(源码)两书中介绍的设计模式与UML图. 整 ...
- mysql 03章_完整性、约束
.完整性:数据库中数据的可靠性有效性和合理性我们称为数据的完整性,这样才能保证数据合理符合现实生活中的数据体现. 注:数据完整性的设计应该在设计表的时候就进行设计了,而不是等到数据库中已经存在数据才进 ...
- META标签的定义与使用(二、页面描述信息(NAME))
二.name的content指定实际内容.如:如果指定level(等级)为value(值),则Content可能是beginner(初级).intermediate(中级).advanced(高级). ...
- Java 集群高可用监控(结合阿里SLB)脚本
欢迎点评,大家一起来优化 计划思路: 只有在mysql slave java 进程状态都正常的情况下才允许nginx 运行, 否则就干掉它, 负载用的是阿里的SLB #bin/bash #邮件函数 ...
- springboot2.x jpa接入多数据源
环境:springboot 2.1.4 数据源引入方式 数据源一 @Configuration @EnableTransactionManagement @EnableJpaRepositories( ...