往期知识点记录:
- 鸿蒙(HarmonyOS)应用层开发(北向)知识点汇总
- OpenHarmony轻量系统服务管理|鸿蒙业务模型重要概念详解
- OpenHarmony轻量系统服务管理|同进程及跨进程间通信的数据结构及过程详解
- OpenHarmony轻量系统服务管理|系统服务间调用之对外接口详解
- OpenHarmony轻量系统服务管理|系统服务管理之基础框架及功能详解
- OpenHarmony轻量系统服务管理|系统服务管理之系统功能管理器详解
- OpenHarmony轻量系统服务管理|消息广播服务及其子功能详解
- OpenHarmony轻量系统服务管理|消息广播功能实例操作详解(一)
- OpenHarmony轻量系统服务管理|消息广播功能实例操作详解(二)
- OpenHarmony POSIX和CMSIS接口适配层解读(1):queue_adapter
- OpenHarmony POSIX和CMSIS接口适配层解读adapter(2):thread/memory/time
- OpenHarmony轻量系统服务管理-samgr主要接口思维导图(1)
- OpenHarmony轻量系统服务管理-samgr主要接口思维导图(2)
- OpenHarmony轻量系统服务管理-samgr:common赏析及实现
- OpenHarmony轻量系统服务管理-samgr:iunknown赏析
- OpenHarmony轻量系统服务管理-samgr:iunknown实现分析
- 持续更新中……
一、前言
承接上一篇iunknown.h的赏析,本文接着对iunknown.c中的实现进行分析。在分析iunknown的实现之前,先介绍一下关于iunknown的相关背景,便于读者对后续代码的理解。
二、IUnknown背景
要想清楚的理解IUnknown就必须知道COM标准到底是什么。
COM全称Component Object Model,即组件对象模型,这是一种遵循COM规范的面向对象编程技术。它包含了三个基本的接口类,分别是IUnknown、IClassFactory和IDispatch。除了IUnknown以外,在鸿蒙后续的代码中,我们也会看到IClassFactory和IDispatch的身影,这里先分析IUnknown。COM规定任何的组件或接口都必须从IUnknown继承,而我在对模块二后续代码的分析中发现了大量的案例,如PubSubInterface、IClientProxy、IServerProxy等。IUnknown包含三个函数,分别是QueryInterface、AddRef、Release。
QueryInterface:查询实现的接口类,需要注意查询完后要调用release解除引用
AddRef:增加引用计数
Release:减少引用计数
看到这里可能会很疑惑,引用计数是什么,有什么作用?我们为什么要使用引用计数?
引用计数牵涉到内存管理的概念,每增加1就代表当前组件被一个对象引用,每减少1就代表当前组件少一个引用者,当引用计数为0时,就代表已经没有对象使用它了,那么它的生命周期就到此结束,释放自己占用的资源。通过引用计数来管理自己的生命周期,不需要其他对象的干预,这种方式极大的减少了代码开发者的工作。这种方式就跟C++11中的智能指针一样,通过引用计数来控制对象的生命周期,减少开发者的工作,避免内存泄漏。
三、代码分析
- 宏定义分析
//从IUnknown实现类的子类对象T(通用宏)获取IUnknown接口对象的指针
#define GET_IUNKNOWN(T) (IUnknown *)(&((T).iUnknown))
- 函数实现分析
为IUnknown对象添加引用
//增加iUnknown接口的引用计数,当重新实现QueryInterface函数时,需要在新的QueryInterface中调用该函数
int IUNKNOWN_AddRef(IUnknown *iUnknown)
{
if (iUnknown == NULL) {
return EC_INVALID;
}
//返回IUnknownEntry类型的地址,通过iUnknown的地址推断
IUnknownEntry *entry = GET_OBJECT(iUnknown, IUnknownEntry, iUnknown);
entry->ref++;//引用计数加1
return entry->ref;
}
查询接口信息
/*
函数功能:查询接口信息
函数参数:@iUnknown:iUnknown对象
@ver:版本号
@target:返回需要的IUnknown的子类类型
函数返回:成功 返回EC_SUCCESS,失败 返回EC_INVALID
函数描述:
1.查询指定版本的IUnknown接口。
2.在获得IUnknown接口对象后,函数调用者使用QueryInterface将该对象转换为所需的子类类型,内部将转换为调用者所需的子类类型。
*/
int IUNKNOWN_QueryInterface(IUnknown *iUnknown, int ver, void **target)
{
//参数检查
if (iUnknown == NULL || target == NULL) {
return EC_INVALID;
}
//返回IUnknownEntry类型的地址,通过iUnknown的地址推断
IUnknownEntry *entry = GET_OBJECT(iUnknown, IUnknownEntry, iUnknown);
//判断版本信息是否有效
if ((entry->ver & (uint16)ver) != ver) {
return EC_INVALID;
}
if (ver == OLD_VERSION &&
entry->ver != OLD_VERSION &&
(entry->ver & (uint16)DEFAULT_VERSION) != DEFAULT_VERSION) {
return EC_INVALID;
}
//作为返回值
*target = iUnknown;
//添加引用
iUnknown->AddRef(iUnknown);
return EC_SUCCESS;
}
释放引用数
/*
函数功能:释放iUnknown对象的引用数
函数参数:@iUnknown:iUnknown对象
函数返回:成功 引用计数,失败 返回EC_INVALID
详细描述:
1.释放不再使用的IUnknown接口的引用
2.在系统提供的默认实现中,如果引用计数为0,则IUnknown接口对象和实现对象的内存不会被释放
*/
int IUNKNOWN_Release(IUnknown *iUnknown)
{
//参数检查
if (iUnknown == NULL) {
return EC_INVALID;
}
//转换为IUnknownEntry对象
IUnknownEntry *entry = GET_OBJECT(iUnknown, IUnknownEntry, iUnknown);
int ref = entry->ref - 1;//引用数-1
if (ref < 0) {
//存在异常
} else {
if (ref == 0) {
//iunknown对象的引用数为0,应删除
//在默认版本中,iunknown可能是全局变量,默认不删除。
} else {
entry->ref = ref;//更新计数
}
}
return ref;
}
写在最后
如果你觉得这篇内容对你还蛮有帮助,我想邀请你帮我三个小忙:
- 点赞,转发,有你们的 『点赞和评论』,才是我创造的动力。
- 关注小编,同时可以期待后续文章ing🚀,不定期分享原创知识。
- 想要获取更多完整鸿蒙最新学习资源,请看下图提示: