Symbian CleanupStack工作机制解析[2]
前面一节主要描述了Symbian OS中清理栈CleanupStack的核心基础结构及工作线路,以在用户线程中创建一个CleanupStack对象为例,详细分析了CleanupStack创建、调用的内部工作机制。本节将说明CleanupStack类针对不同对象,提供的不同方法,在发生Leave时的不同动作。
Symbian OS提供用户操作清理栈的接口通过CleanupStack类展示,全部为静态方法。将对象压入清理栈的方法有CleanupStack::PushL(CBase*)、CleanupStack::PushL(TAny*)和CleanupStack::PushL(TCleanupItem &)三种。从方法的不同传入参数可基本看出,针对不同的对象类型,压栈方法将调用不同的PushL重载方法来实现不同对象的入栈,当然其目的不在于如何进栈,而在于当发生Leave时,之前被压入栈的对象将做不同的处理,接下去就将详细说明不同类对象Leave时的不同处理。
当对象继承自Cbase类时,将该对象压入清理栈时会调用CleanupStack::PushL(CBase*)重载方法。
class CHeapClass : public CBase
{
public:
~CHeapClass();
static CHeapClass* NewL();
static CHeapClass* NewLC();
void FuncL();
private:
CHeapClass();
void ConstructL();
private:
TUint8* iBuf;
};
void CheapClass:: ConstructL()
{
……
iBuf = new TUint8[100];
……
}
CheapClass:: ~CHeapClass ()
{
if(iBuf != NULL)
delete[] iBuf;
}
通常,当程序中需用到指向堆对象的局部变量时,为防止内存泄露,需在调用可能发生Leave的方法前,将该对象压入当前线程的清理栈中,如下:
void LocalFunc()
{
CheapClass *obj = CheapClass::NewL();
CleanupStack::PushL(obj);
Obj-> FuncL();
CleanupStack::PopAndDestroy();
}
当调用代码FuncL出现Leave或者调用CleanupStack::PopAndDestroy()时,根据清理栈机制,此时就会释放相应对象。由于之前压入的对象obj是CBase的派生类,该类的特点就是具有需析构函数。所以,当发生Leave时,清理栈类调用delete方法来删除该对象,delete方法相应地能正确地调用到该派生类的析构函数,这样就能完成释放该对象及该对象所占有的资源。
对于非CBase派生类,当压栈时调用CleanupStack::PushL(TAny*)方法。此时,当发生Leave或者调用CleanupStack::PopAndDestroy()时,清理栈内部所做的工作不同于上述情况。清理栈在清理对象时,只是简单的调用User::Free()来释放该对象。该重载方法适合没有析构函数的类对象(该对象没有自己的独占资源需要释放),因为调用User::Free()方法不会导致类析构函数被调用。
对于特殊情况,当调用出现Leave时,代码不仅仅要做简单的类对象释放,很明显上述方法都无法满足。Symbian OS中提供如CleanupClosePushL,CleanupDeletePushL和CleanupReleasePushL等方法,来补充适合非类对象内存释放的情况。对于这些方法的调用,其核心就是清理栈的第三种接口CleanupStack::PushL(TCleanupItem &)的应用。
TCleanupItem(TCleanupOperation anOperation)
当将一个TcleanupItem对象压入清理栈后,程序调用出现Leave或者调用
CleanupStack::PopAndDestroy()时,在退出TRAP前,代码有机会在TcleanupItem
对象中定义的TcleanupOperation方法中先得到异常清理,TcleanupOperation方法定义如下:
typedef void(* TCleanupOperation)(TAny*)
在自定义的TcleanupOperation方法中,用户有机会对程序异常做更多复杂和自定义的异常处理,而不是简单地只调用delete方法或者User::Free()来完成内存释放功能,在该方法,用户可以同时释放内存,释放该对象所占有的Symbian资源等。
Symbian OS
提供的
CleanupReleasePushL()等方法,封装了其中内部工作机制,使得用户可以方便的完成相应自定义清理方法。
class RTest;
{
public :
void Release();
}
RTest testObj;
CleanupReleasePushL(testObj);
…… //Some Leave Func
CleanupStack::PopAndDestroy();
当调用CleanupReleasePushL(testObj)时,内部调用了工具类CleanupRelease:: PushL()
方法,创建了一个
TCleanupItem对象,并用对象testObj的void Release()方法初始化TCleanupItem对象的TcleanupOperation
方法,同时将该
TCleanupItem压入清理栈。当程序Leave或者调用CleanupStack::PopAndDestroy()时,对象testObj的void Release()方法将被调用,用户可在该方法中做相应的异常处理。
总结,当通过CleanupStack::PushL(CBase*)将CBase派生类压入清理栈后,程序Leave时,该类的析构函数能被及时调用,能有机会释放该对象占有的内存资源;当通过CleanupStack::PushL(TAny*)方法将非CBase类对象压入清理栈后, 程序Leave时,清理栈仅简单的调用User::Free()方法释放该对象,而不会导致析构函数被调用;当通过CleanupStack::PushL(TCleanupItem &)方法将TCleanupItem对象压入清理栈后,程序Leave时,TCleanupItem对象中定义的清理函数将被调用,可完成一些复杂情况下的异常清理。
开发提示:
如上节所描述,TRAP宏有较大的内存调用开销。所以,在代码中尽量控制频繁使用TRAP宏。在一些逻辑上不可分割的操作上,可利用CleanupStack::PushL(TCleanupItem &)重载方法,在程序发生Leave时,在自定义的异常处理方法中控制程序逻辑,而不采用TRAP宏判断。