65.9K
CodeProject 正在变化。 阅读更多。
Home

Palm Memory Manager API

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.33/5 (3投票s)

2002年11月6日

CPOL

5分钟阅读

viewsIcon

78308

downloadIcon

157

如何使用 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 函数中创建,然后在 SayHelloSayGoodbye 函数中锁定。从锁定返回的指针随后传递给 DrawText 函数以显示在屏幕上。从绘图函数返回后,句柄或指针将被解锁。该句柄在 MainFormClose 函数中释放。

从代码中您会注意到,myHandle 正使用 MemHandleResize 进行大小调整,其中还包含 MemSetMemMove 的调用,以便您可以看到它们用法的示例。演示中没有使用 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 特定的其他相关主题。

© . All rights reserved.