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 ...
随机推荐
- ionic:安装
ylbtech-ionic:安装 1.返回顶部 1. ionic 安装 本站实例采用了ionic v1.3.2 版本,使用的 CDN 库地址: <link href="https:// ...
- Openstack-L 路由注入方式
目录 目录 前言 从 Commands 到 Action 操作函数 前言 Openstack 新旧版本提供了不同的路由注入方式,也就是 Route Module 的代码方式不同,就二次开发而言用那一种 ...
- Openstack组件实现原理 — OpenVswitch/Gre/vlan
目录 目录 前文提要 Neutron 管理的网络相关实体 OpenVswitchOVS OVS 的架构 VLan GRE 隧道 Compute Node 中的 Instance 通过 GRE 访问 P ...
- elasticsearch的基本用法(转载)
本文出自:http://blog.csdn.net/feelig/article/details/8499614 最大的特点: 1. 数据库的 database, 就是 index 2. 数据库 ...
- spark-sql性能优化之——动态实现多个列应用同一个函数
在对一个dataframe的多个列实现应用同一个函数时,是否能动态的指定? 例如: 对A,B,C三列实现分组统计 1.初始化spark,构建DF val spark = SparkSession.bu ...
- Spring Boot 文件下载
1. 文件下载类 import javax.servlet.http.HttpServletResponse; import java.io.*; public class DownloadUtil ...
- 22-Ubuntu-文件和目录命令-移动文件和目录-mv
mv 可以用来移动文件或目录,也可以给文件或目录重命名. 选项 含义 -i 覆盖文件前提示 1.移动文件和目录 1-1移动文件 例: 将当前目录下的123.txt文件移动到~/桌面/b/e目录下 mv ...
- HDU 3607 线段树+DP+离散化
题意:从左往右跳箱子,每个箱子有金币数量,只能从矮处向高处跳,求最大可获得金币数,数据规模1<=n<=1e5. 显然是一个dp的问题,不难得出dp[ i ] = max(dp[j] )+v ...
- windows下mysql8.0.x简单安装!
1.官网下载mysql安装包并解压到自己喜欢的目录 2.在解压的目录下,添加my.ini配置文件,内容如下:[mysqld]# 设置3306端口port=3306# 设置mysql的安装目录 下面是我 ...
- 7.ICMP与ping
ping是基于ICMP(Internet Control Message Protocol)协议工作的 ICMP报文封装在IP包里,作为“侦察兵”,非常轻巧. ICMP报文的类型有很多,最常用的是 ...