在第一次接触brew时,我就在疑问:“brew中的接口是个什么概念啊!”,而且发现它里面就没有数据,只有一个指针。后来慢慢才熟悉和明白了brew的接口的实质和内存布局。
如果你要揪跟问底,那你可以跟踪到其定义的头文件中看看究竟。那我就以IFileMgr为例子来看看究竟!(别的都类似)
当我跟踪进去后,我发现头文件






































中都是一些宏定义,主要就是QINTERFACE(IFileMgr) 宏,我将其一层层展开:





























原来就是个IFileMgr就是个结构体,结构体中只有一项就是另一个结构体的指针,现在突然明白了,为什么原来sizeof(IFileMgr)
= 4呢?那么这个指针指向的结构体是什么东西啊,看看里面的定义都是一些函数指针,仔细分析会发现所有的函数指针都是这个接口提供的函数!
第一项是个宏:INHERIT_IBase(IFileMgr);
我不想多说了,这个其实就是IBase接口提供的引用记数功能,每个接口的第一项就是它。
现在有个疑问了,那么这个接口的数据在那里放着呢?废话啊,不在头文件中就在实现文件中。对,这点不容质疑。但是究竟这个数据是怎么和函数联系起来的呢?那我就要猜测了!(不猜测也不行,因为实现文件编译成二进制了)。高通对知识产权保护的真是到位啊(这是他一贯的作风)!怎么猜测呢?
我先看看怎么使用一个接口。大部分接口都是通过ISHELL_CreateInstance(IShell * pIShell, AEECLSID cls,
void** ppobj ) 创建的。然后就使用这个创建的pobj调用函数如OpenFile(IFileMgr * piname, const char
*pszFile, OpenFileMode
mode)。每个函数的第一个参数就是接口指针。正如前面分析的宏,这个函数也是个宏定义,变成调用虚表中相应的函数指针指向的函数。指向的函数就是真真正正实现的函数。
那么现在考虑2个问题:
1. 在写这些函数时如何得到数据的存储地址,你只是有一个虚表的地址,那我想这些
数据应该是紧放在虚表后面的空间中连续存放,否则函数无法得到数据。
2.函数指针应该初始化,以指向真实的实现函数,而且还要为数据和虚表分配空间。
我们可以肯定第1个问题是正确的。那么2个问题在那里做这些工作呢?我猜测的是ISHELL_CreateInstance中,因为ISHELL_CreateInstance返回时,接口已经建立好了,可以调用接口的函数了!如果不在ISHELL_CreateInstance中,那恐怕函数都调用不了啊!!
我尝试写了一个大概示意代码:





























































如果你看懂上面的函数,说明你理解了接口了吧!