Palm Memory Manager API






4.33/5 (3投票s)
如何使用 Palm 的内存管理器 API 为 Palm 手持设备应用程序进行动态内存分配。
引言
Palm 手持设备的内存管理与您可能习惯的标准 C/C++ 和 Windows 内存管理有很大不同。本文将尝试阐明 Palm 内存的“疯狂”世界。
内存有限
尽管较新的 Palm 设备拥有越来越多的 RAM,但大多数仍低于 16MB,并且没有硬盘。所有应用程序和数据库都存储在 RAM 中,因此 Palm 设备必须以与 PC 略有不同的方式进行操作。我们总不希望我们的应用程序覆盖永久存储,对吧?
RAM 分为两个部分:存储区和动态区(或动态堆)。顾名思义,存储区是应用程序和数据库等永久数据所在的位置,由数据库管理器处理。动态堆用于 Palm OS 全局变量、Palm OS 动态分配、您的应用程序的全局变量、您的应用程序的堆栈空间以及您的应用程序进行的任何动态分配。动态堆由内存管理器处理,其大小取决于您的 Palm 设备拥有的 RAM 量、操作系统版本以及您安装的应用程序。
由于动态内存量有限,Palm OS 需要能够移动内存块以保持其空闲空间连续,从而有足够的空间进行新的分配。因此,有两种类型的内存块:*指针*和*句柄*。指针是不可移动的内存块,而句柄可以被 Palm OS 移动。建议尽可能使用句柄。
内存管理器 API
要操作 Palm 上的内存,您必须使用内存管理器 API。
使用句柄
让我们首先看看如何使用 API 分配新的内存句柄。
MemHandle MemHandleNew (UInt32 size)
函数返回一个新分配的MemHandle
,其大小(以字节为单位)在 size 参数中指定。*FYI:您不能分配大于 64KB 的单个内存块。*
由于操作系统可以随意移动句柄,因此您需要“锁定”它才能对其进行读写。
- 使用
MemPtr MemHandleLock (MemHandle h)
锁定句柄并获取指向句柄内存块的MemPtr
(指针)。
使用锁定的句柄后,您必须快速“解锁”它,以便操作系统可以再次移动它。
- 要解锁先前锁定的句柄,请使用
Err MemHandleUnlock (MemHandle h)
。
当您完成对动态创建的句柄的使用后,需要调用 Err MemHandleFree (MemHandle h)
来释放它。
以下是使用句柄的内存管理器函数的示例
/* example of handle allocation and use */ MemHandle myHandle = MemHandleNew(13); // create a 13-byte handle Char* myStr = (Char*)MemHandleLock(myHandle); // lock the handle before use StrCopy(myStr, "Hello World!"); // use it MemHandleUnlock(myHandle); // unlock the handle MemHandleFree(myHandle); // free the handle
当句柄被锁定后,操作系统会为该句柄增加一个*锁定计数*。您可以锁定一个句柄最多 14 次,然后会收到“块被过度锁定”错误。您对句柄执行的每次锁定,都必须进行相应的解锁。如果您解锁句柄的次数多于锁定计数,您将收到“块被过度解锁”错误。
MemHandleUnlock
函数的替代方法是 Err MemPtrUnlock (MemPtr p)
。如果您只想将锁定的指针传递给另一个函数而没有句柄,则此方法非常方便。它会像 MemHandleUnlock
一样减少被锁定句柄的锁定计数。
使用指针
要分配一个不可移动的内存块(指针),请使用 MemPtr MemPtrNew (UInt32 size)
。*FYI:请记住,您不能分配大于 64KB 的单个内存块。*
当您完成对指针的使用后,调用 Err MemPtrFree (MemPtr p)
来释放该内存块。
以下是使用指针函数的示例
/* example of handle allocation and use */ Char* myStr = (Char*) MemPtrNew(13); // allocate a 13-byte pointer StrCopy(myStr, "Hello World!"); // use it MemPtrFree(myStr); // free the pointer
如果需要在整个应用程序中使用内存,或者分配的内存生命周期较短,则应使用指针而不是句柄。请记住,频繁地锁定和解锁句柄也会带来比指针分配更高的性能成本。
其他内存函数
Err MemSet (void* dstP, Int32 numBytes, UInt8 value)
将 dstP
指针的 numBytes
设置为 value
(就像 C++ 的 memset
一样)。Err MemMove (void* dstP, const void* sP, Int32 numBytes)
将 sP
源指针的 numBytes
移动到 dstP
目标指针,并自动处理重叠范围。
UInt32 MemHandleSize (MemHandle h)
返回句柄的大小(以字节为单位)。
UInt32 MemPtrSize (MemPtr p)
返回指针的大小(以字节为单位)。
要调整未锁定的句柄大小,请调用 Err MemHandleResize (MemHandle h, UInt32 newSize)
。如果您要增大句柄,并且句柄之后没有足够的可用空间,则操作系统会将该块移动到新的位置。
如果您需要调整指针的大小,请调用 Err MemPtrResize (MemPtr p, UInt32 newSize)
。仅当您要缩小指针或指针之后有足够的可用空间时,才会调整指针的大小。此函数也可用于给定的指针上锁定的句柄。
演示
我修改了我第一篇 Palm 文章 Palm 手持设备开发入门 中的“Hello World!”应用程序,以演示如何使用各种内存管理器函数。与之前一样,该演示是用 Metrowerk 的 CodeWarrior IDE(版本 6)编译的。
添加了一个名为 myHandle 的静态/全局 MemHandle
,它将保存要在屏幕上显示的文本。该句柄在 MainFormInit
函数中创建,然后在 SayHello
或 SayGoodbye
函数中锁定。从锁定返回的指针随后传递给 DrawText
函数以显示在屏幕上。从绘图函数返回后,句柄或指针将被解锁。该句柄在 MainFormClose
函数中释放。
从代码中您会注意到,myHandle 正使用 MemHandleResize
进行大小调整,其中还包含 MemSet
和 MemMove
的调用,以便您可以看到它们用法的示例。演示中没有使用 MemPtrResize
,因为它不一定总是有效。它本质上与调整句柄大小相同。
这是主要的几段代码亮点
// need a global handle for demo purposes static MemHandle myHandle = NULL; static short nCount = 0; // This routine draws text on the center of the form static void DrawText(Char* pText) { short nCharWidth = 0; short width = 0, height = 0; // get the width of the string nCharWidth = FntCharsWidth(pText, StrLen(pText)); // get the width and height of the string WinGetWindowExtent(&width, &height); // draw the text in the center WinDrawChars(pText, StrLen(pText), (width/2) - (nCharWidth/2), height/2); } // This routine draws "Hello World!" on the form static void SayHello() { // resize the handle only if you need to if (MemHandleSize(myHandle) == 13 || MemHandleResize(myHandle, 13) == 0) { // lock the handle before use Char* pText = (Char*) MemHandleLock(myHandle); // make sure none of the previous string is left in MemSet(pText, 13, 0); // copy Hello World! into the char ptr StrCopy(pText, "Hello World!"); // pass the pointer and draw it on the screen DrawText(pText); // unlock the handle MemHandleUnlock(myHandle); } } // This routine draws "Goodbye World!" on the form static void SayGoodbye() { nCount++; // resize the handle only if you need to if (MemHandleSize(myHandle) == 15 || MemHandleResize(myHandle, 15) == 0) { // lock the handle before use Char* pText = (Char*) MemHandleLock(myHandle); // make sure none of the previous string is left in MemSet(pText, 15, 0); // copy Goodbye World! into the char ptr StrCopy(pText, "Goodbye World!"); // Lets use MemMove if (nCount%2 == 0) { Char* pTemp = (Char*) MemPtrNew(15); StrCopy(pTemp, "Bye bye World!"); MemMove(pText, pTemp, 7); MemPtrFree(pTemp); pTemp = NULL; } // pass the pointer and draw it on the screen DrawText(pText); // unlock the pointer in this function MemPtrUnlock(pText); } } // This routine initializes the MainForm form. static void MainFormInit(FormType* frmP) { // we're going to lock this handle in our SayWhat functions // then we'll pass the pointer to the DrawText function myHandle = MemHandleNew(20); // allocate 20 bytes } // This routine closes the MainForm form. static void MainFormClose() { // free the handle if (myHandle != NULL) { MemHandleFree(myHandle); myHandle = NULL; } }
结论
根据我自己的经验,Palm 内存处理可能相当棘手。撰写本文帮助我更好地理解了整个系统,希望也能帮助您。
敬请期待我与 Christian Graus 在不久的将来发布的更多 Palm 文章。我们计划涵盖 Palm 数据库、自定义绘制列表、表格、Palm 同步器以及 PalmOS 特定的其他相关主题。