赞
踩
在完结SA服务框架初始化之前,我们先来补补课,看看这个频繁出现在代码中的结构体,弄清楚它们能让我们阅读源码时更加得心应手,也能够体会到鸿蒙开发人员设计函数和结构体时的巧妙构思,开拓眼界
这里先穿插一个非常重要的知识点——有利于后面讲解的展开
void关键字我们都知道是指定没有没有可用的值。在前面的文章中提到了void在鸿蒙OS中的一个妙用——通过在函数体中void(parameter)使得那么没有传入实参的形参不会导致编译warning
在common.c和其他函数中我们经常看到这样的写法:
在Vector的结构体中data的类型:
所以这里总结一下void的常见用法:
也就是是说void类型的指针能够指向任意类型的指针,一般用于数据封装和通用设计,而Vector能够管理不同类型比如serviceImpl或者featureImpl就是因为使用了void类型指针的缘故
主要的相关文件在common.h和common.c中
首先在common.h中可以看到其详细的结构体:
typedef struct SimpleVector{
int16 max;
int16 top;
int16 free;
void **data;
VECTOR_Key key;
VECTOR_Compare compare;
}
typedef void *(*VECTOR_Key)(const void *);
typedef int (*VECTOR_Compare)(const void *, const void *);
两个指针的使用主要在vector的创建之中,下面会结合例子进行说明
说完结构体,那么就可以看看与之相关的操作了
函数很简单,仅仅是简单的赋初值并返回对象Vector
但是真正的使用却略微讲究,在g_samgrImpl的初始化时是这样使用它创建vector的:
可以看到第一个参数是调用了函数GetServiceName,但是没有传入具体的serviceImpl,所以返回的仍然是NULL,但是我认为这样写肯定也是有用意的,不然为什么不直接写NULL,由于代码没有看完这里不妄加猜测,后面再做分析;第二个则是将string中的函数strcmp用于实现VECTOR_Compare。
函数功能:对于一个vector结构体空间的释放,先调用SAMGR_Free将data所指的空间释放,再将属性清空
非常重要的一个函数:将element添加到data[top]中
具体的函数流程如下:
1. 当top<max时,将element直接放入data[top]位置上,返回top++
2. 当top>=max时,因为可能存在free的空间,所以进行循环,当有空闲位置(data[i]==NULL)时将element放入,并返回位置i——通过registerService函数我们可以知道返回的i后面存储为serviceId,所以这里一定是返回的当前存储对象的位置
3. 倘若没有找到空闲的位置,则需要对vector进行空间扩展以容纳新的对象
4. 分配新空间——大小为原来空间+4
5. 将原来data中的数据拷贝到新空间中
6. 将新空间更新到vector中
7. 重复第一步的操作,注意这是是先返回top的位置表示当前存储element的位置,然后再++用于下一次对象存储的开始top起点
我们可以看到初始化的时候data是NULL,所以设计这样的添加函数能够最大化有效的利用空间
这里有个思考的小插曲:
在判断top>=max后,通过循环遍历查找是否有free的空间这个流程中。我想的是如果提前判断free是否<=0,<=0则没有空闲空间就不需要循环遍历直接往下开辟新空间进行赋值,而如果free>0则再进行for循环。
不知道这样在工程上是不是能够提高那么一点点效率
函数功能:根据所给的index返回data[index]
注意这里返回的对象也是指针,看到返回的类型是void指针类型,说明可以返回任意类型——也就是先前存入的指针类型——返回void指针可以使函数具有很好的通用性,各种类型的指针都能够通过vetor进行管理——但是使用的时候一定要使用正确的指针类型进行接收
该函数完成两件事:首先将index对应的element提取并返回;其次如果传入新的element则将其覆盖index对应的旧element,传入NULL则free++表示多一块空闲空间,将旧element对应位置指针置为NULL
这两个函数较为简单,一个返回当前vector的总空间大小,一个返回vector中存储管理了多少element
两个函数放到一起讲,都是查找element对应index的函数
首先看到第一个函数
最后一行的三目运算比较有意思:
在调用函数VECTOR_FindByKey传入第二个参数的时候判断vector->key是否存在——这又要追溯到vector建立时该属性的初始化是使用传入的参数key——再往上找,在init的时候是使用GetServiceName,没有传入参数(NULL),因为init的是全部服务的samgrImpl所以不能给定某个确定的serviceName。但是每个serviceImpl中间有对应的service类型。
所以我猜想使用三目运算是为了适配不同级别vector的使用,如果key的类别可以统一则通过vector->key(element)将element转变为统一的key类型便于后面的查找和compare;如果没有统一的key,则还是传入element
然后我们看向第二个函数
从函数体的内容来看,循环给到的data数组,跳过NULL的data[i],如果存在vector->key则进行类型转换再进行data[i]与所给key的比较,否则直接进行比较;如果存在compare则再使用其进行比较;匹配成功则返回对应的位置i
这个函数使用的较多,在registerService中有使用,我们看看用法:
int16 VECTOR_FindByKey(Vector *vector, const void *key)
static BOOL RegisterService(Service *service){
……
int16 pos = VECTOR_FindByKey(&(samgr->services), (void *)service->GetName(service));
……
}
在注册的时候将samgr->services传入(类型为Vector)并且传入了(void *)service->GetName(service))如果以broadcast为例,这里返回的是const char * “Broadcast”
这里又有点疑惑了,原来我们意味vector中存入的应该都是对应serviceImpl的指针,而传入的是一个字符串,字符串应该是无法和serviceImpl类型指针匹配的(难道是为了使用下面的strcmp?)
而且我们知道g_samgrImpl->services对应的key是没有初始化的,所以也不能进行统一的key类型转换,那么这里的查找就有些迷惑了
虽然在登记函数中使用的时候期望的是pos找不到返回-1——因为并没有注册该函数,这样使用的结果没有问题
可能还是代码的理解不够完全,后续有机会还会深入思考这个问题
至此Vector讲解完毕,不妥之处,还望批评指正
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。