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 ...
随机推荐
- c实现swap函数陷阱
swap函数陷阱 使用c实现一个交换两个数的函数,代码很简单: void swap(int *a, int *b) { *a ^= *b; *b ^= *a; *a ^= *b; } 只有3行代码,且 ...
- Netty环境安装配置
本章中介绍的Netty开发环境的安装及配置; 这个一系列教程示例的Netty最低要求只有两个:最新版本的Netty 4.x和JDK 1.6及更高版本. 最新版本的Netty在项目下载页面中可找到:ht ...
- Map获取键值,Map的几种遍历方法 (转载)
Map类提供了一个称为entrySet()的方法,这个方法返回一个Map.Entry实例化后的对象集.接着,Map.Entry类提供了一个getKey()方法和一个getValue()方法,Map.E ...
- Jedis整合单机、Sentinel和Cluster模式
配置文件和配置类 @Data @Configuration @ConfigurationProperties("jedis-config") public class JedisC ...
- sklearn提供的自带数据集
sklearn 的数据集有好多个种 自带的小数据集(packaged dataset):sklearn.datasets.load_<name> 可在线下载的数据集(Downloaded ...
- numpy基本函数
在学习python的时候常常需要numpy这个库,每次都是用一个查一个,这个,终于见到一个完整的总结了http://blog.csdn.net/blog_empire/article/details/ ...
- Flask-session用法
概念 flask-session是flask框架的session组件,由于原来flask内置session使用签名cookie保存,该组件则将支持session保存到多个地方,如: * redis:保 ...
- override和overload区别
方法重载(overload)实现的是编译时的多态性(也称为前绑定) 方法重写(override)实现的是运行时的多态性(也称为后绑定)
- Java类加载器浅述
jdk默认提供了三种类加载器: 1.Bootstrap ClassLoader(引导类加载器): 将<JAVA_HOME>\lib目录下的类库加载到虚拟机内存中,用来加载java的核心库, ...
- 关于Cadence OrCad 16.6的破解
相信很多人都知道去老吴的博客上找安装包和破解文件,但是上面的自称一键式破解程序.以及破解图文说明,都是很有问题的. 首先,该一键式破解程序默认的文件后缀与该程序指向的安装压缩包后缀不一致:其次,该程序 ...