初学Brew时,最烦人的就是接口的定义了,一大堆的宏,让人不知所云。这里,将以helloworld为例,我们一步一步揭开它的神秘面纱。
1.helloworld:
typedef struct _helloworld {
AEEApplet a ; //第一个成分必须是 AEEApplet
AEEDeviceInfo DeviceInfo; // 设备信息
IDisplay *pIDisplay; // 显示
IShell *pIShell; //指向Shell
//加入自己的变量
} helloworld;
2.AEEApplet
看看, 什么是AEEApplet呢?
typedef struct _AEEApplet AEEApplet;
struct _AEEApplet
{
DECLARE_VTBL(IApplet)
AEECLSID clsID;
uint32 m_nRefs; // Applet reference counter
…… //其它的变量定义
};
DECLARE_VTBL(IApplet)又是什么?
它的宏定义为:
#define DECLARE_VTBL(iname) iname vt##iname;
解开后为
struct _AEEApplet
{
IApplet vtApplet; //定义了一个接口
AEECLSID clsID;
uint32 m_nRefs; // Applet reference counter
…… //其它的变量定义
};
3.IApplet
IApplet又是什么东东呢? 这个比较难找。
在AEE.h中找到如下代码:
typedef struct _IApplet IApplet;
#define INHERIT_IApplet(iname) /
INHERIT_IBase(iname); /
boolean (*HandleEvent)(iname * po, AEEEvent evt, uint16 wp, uint32 dwp)
QINTERFACE(IApplet)
{
INHERIT_IApplet(IApplet);
};
#define IAPPLET_AddRef(p) GET_PVTBL(p,IApplet)->AddRef(p)
#define IAPPLET_Release(p) GET_PVTBL(p,IApplet)->Release(p)
还是看看解开后是什么吧:
struct _IApplet {
struct IAppletVtbl *pvt;
};
typedef struct IAppletVtbl IAppletVtbl;
struct IAppletVtbl
{
uint32 (*AddRef) (IApplet*);
uint32 (*Release) (IApplet*);
boolean (*HandleEvent)(IApplet * po, uint16 evt, uint16 wp, uint32 dwp);
};
至于宏定义
#define IAPPLET_AddRef(p) GET_PVTBL(p,IApplet)->AddRef(p)
展开来后则成了:
#define IAPPLET_AddRef(p) (IApplet *)p->pvt->AddRef(p)
我们看到IApplet的定义中,只有一个变量,是一个指向vbtl的指针,名字为pvt,这就解释了为什么我们的Applet也会有一个pvt指针啦。
因为helloworld的第一个变量为IApplet类型的,当我们对helloworld时行向上转型到IApplet时,我们就可以使用IApplet类型中定义的成员变量。可以理解成C++中的子类与父类的继承关系。
4.AEEApplet_New
再回到AEEAppGen.c中,我们就能更好的理解AEEApplet_New的思想了。
在分配Applet内存时,分配的并不仅仅是helloworld结构内存的大小,而是helloworld结构大小再加上IappletVtbl的大小。
(AEEApplet*)MALLOC(nSize + sizeof(IAppletVtbl))
从前面的定义我们知道,IAppletVtbl实际是一组函数指针,给vtbl分配了内存后,就要对它进行初始化,让它指向实际的函数地址。这样才可以通过我们前面的宏定义来执行实际的函数:
如#define IAPPLET_AddRef(p) (IApplet *)p->pvt->AddRef(p)
这一段代码如下:
appFuncs = (IAppletVtbl *)((byte *)pme + nSize);//让appFuns指向实际分配的内存块
//Initialize the individual entries in the VTBL
appFuncs->AddRef = AEEApplet_AddRef; //实际的AddRef函数地址
appFuncs->Release = AEEApplet_Release;//实际的Release函数地址
appFuncs->HandleEvent = AEEApplet_HandleEvent;//实际的HandleEvent地址
下面就要对Iapplet的pvt进行符值,让它指向这一块vtbl啦。
INIT_VTBL(pme, IApplet, *appFuncs);
宏展开再看看:
((IApplet *)pme)->pvt = (IAppletVtbl *)appFuncs;
5.结论:
由此我们可以看到,在brew中,所谓的接口,不过是一个指针,不同类型的接口的指针指向了不同的的函数指针表,可以理解为C++中的Vtbl。
对于某一接口的的实例,它的第一个变量就是指向Vtbl的指针,一般来讲,它的Vtbl表是存在这个类实例的尾端的。在这个类初始化时,必须同时对它的vtbl也要初始化。