可写静态数据在EKA1的DLL中是不允许的,因为EKA1中,DLL具有分离的程序代码和只读数据区域,没有可写数据的区域。
EKA2中,支持在DLL中使用可写静态数据,但不建议使用,因为从内存使用看它的代价很大。
本文只讨论EKA2的情况,对EKA1中的情况不做讨论。
正常情况下,如果在DLL中加入可写静态数据(如全局变量)当编译真机版DLL代码时,返产生编译错误"XXX.dll' has initialised data."。需要注意:S60 V3 FP2及以上的版本中只声明定义一个可写静态数据是不会报错的,只有在DLL中使用该数据才会报错。
DLL如何使用可写静态数据?
可写静态数据Writable static data,下面以WSD简写。
1. 在DLL的MMP文件中添加关键字EPOCALLOWDLLDATA,可以正常使用WSD,但是不推荐使用。后面介绍
2. 用TLS,推荐也是通用方法,但是存在效率问题。后面介绍
3. 通过类封装WSD,并把类作为参数传递给使用者
4. 封装客户端/服务器结构,利用exe可有全局变量的特点
为什么不推荐直接使用WSD?
这是因为对于每个载入DLL的进程,DLL中的WSD通常需要消耗4KB的内存。
当进程载入第一个包含WSD的DLL时,它创建一个单独的虚拟内存区域来保存该WSD。一个虚拟内存区域最小为4KB,并且它的大小与WSD占用空间大小无关。4KB中WSD实际占用空间之外的内存都被浪费了。但是,如果随后的其它DLL载入该进程并且也使用WSD,那么这个虚拟内存区域可供其它DLL使用, 而不用为每个使用WSD的DLL都分割4KB的虚拟内存区域。
既然内存是针对进程而言,那么潜在的内存浪费量为:(4KB – WSD 字节) × 客户端进程数
如果你创建一个只被一个应用程序载入一次的DLL,那么开销还算可以接受的。但是,如果你的DLL可能被许多进程使用,带来的内存开销和限制就不太值得了。
除了内存问题,Symbian模拟器并不完全支持使用WSD的DLL,在单独的正在运行的进程中,模拟器只能载入一个使用WSD的DLL(原因是Symbian OS模拟器在Windows上层的实现方式)。如果在仿真器中有第二个进程试图载入相同的DLL,该操作会失败并返回错误代码 KErrNotSupported。
可能被忽略的使用WSD的情况
有时候你会发现自己不自觉地使用WSD,一些Symbian OS类具有非平凡的构造函数,这意味着其对象必须在运行时构造。这里是一些示例:
static const TPoint KGlobalStartingPoint(50, 50);
static const TChar KExclamation('!');
static const TRgb KDefaultColour(0, 0, 0);
也就是说KGlobalStartingPoint只有在运行时才被构造,那么它的值并不是常量,是根据运行时刻分配不同的内存块的。所以如上的声明定义也算是使用WSD。
线程本地存储TLS(Thread Local Storage)
static TInt SetTls(TAny* aPtr); // 设置TLS数据
static TAny* Tls(); // 获得保存于TLS中的指针
static void FreeTls(); // 清除TLS数据
关于Tls效率的问题