对于在Symbian平台上开发GUI或者Server程序,CleanupStack已由框架创建,用户可直接使用CleanupStack::PushL()、CleanupStack::Pop()等方法来控制可能的异常。在这样的框架下,用户无法了解CleanupStack是如何被创建,是如何工作的。
当在Symbian平台上开发Console程序或者使用多线程时,异常清理栈CleanupStack就必须由用户自己创建和维护。Symbian封装了其中的核心机制,使得用户可以非常方便的创建CleanupStack清理栈并使用它。典型的代码如下:
- TInt ThreadEntry(TAny *arg) //用户线程的入口函数
- {
- TInt retCode = 0; //函数异常退出的错误代码
- CTrapCleanup* cleanupstack = CTrapCleanup::New(); //创建CleanupStack清理栈
- if(cleanupstack == NULL)
- return -1;
- TRAP(retCode,Fun_EntryL()); //捕获Fun_EntryL()异常退出
- If(retCode != KerrNone)
- {
- //Handle_Error();
- }
- delete cleanupstack;
- return retCode;
- }
- Void Fun_EntryL()
- {
- CTestObj *obj = CTestObj::NewL(); //创建CTestObj时可能Leave
- CleanupStack::PushL(obj);
- Obj->FuncMayLeaveL(); // FuncMayLeaveL()方法调用可能Leave
- CleanupStack::PopAndDestroy();
- }
上述主要的方法已做大致的说明,接下去具体分析CleanupStack清理栈创建和工作过程。CleanupStack类只是Symbian OS提供的针对清理栈的静态类,也就是工具类,Symbian OS中真正的清理栈功能是由CCleanup实现的。但如上述代码,我们在创建清理栈时并没有直接创建CCleanup对象,而使用CTrapCleanup::New()方法创建了一个CTrapCleanup对象,这其中就是核心所在。
当调用CTrapCleanup::New()方法时,真正做了什么?首先,要使用清理栈,则必须创建CCleanup对象;其次在CTrapCleanup中创建了一个TCleanupTrapHandler对象。在创建了这些对象后,调用User::SetTrapHandler()将生成的TCleanupTrapHandler安装到当前线程中以备后用。
在用户线程中,当调用可能Leave的方法时,必须用TRAP/TRAPD宏加以捕获。TRAP/TRAPD宏调用TCleanupTrapHandler::Trap()以标志开始异常捕获,当被TRAP宏监视的函数Leave时,调用TCleanupTrapHandler::Leave()方法控制其CCleanup对象完成之前已压入清理栈的对象;当被TRAP监视的函数正常时,调用TCleanupTrapHandler::UnTrap()方法取消异常捕获。
同样,在可能Leave的方法中,如上述的Fun_EntryL(),当调用CleanupStack::PushL(obj)方法时,其内部是通过User::TrapHandler()获得当前线程中已安装的TCleanupTrapHandler对象,然后通过TCleanupTrapHandler::Cleanup()获得清理栈类,最后由清理栈类真正完成压栈、出栈和异常时栈内对象的内存释放。
总结,清理栈的工作是以一个TRAP/TRAPD为单位的,在被某个TRAP宏监视的代码段内压入清理栈的对象,当出现异常Leave时,这些对象都能通过清理栈完成内存释放,不会导致内存泄露。但在不同TRAP宏压入的对象,在上述情况下是无法释放的,这点需要注意