Symbian CleanupStack工作机制解析

本文深入探讨了SymbianOS平台上的异常处理机制CleanupStack,包括其创建过程、内部工作原理以及如何根据不同对象类型进行有效资源清理。
 

对于 Symbian OS 中使用的 CleanupStack 机制,在这里不作好坏的评价,既然选择了在 Symbian 平台上开发,那最重要的就是了解它的机制,掌握并高效的利用好它。

       对于在 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 宏压入的对象,在上述情况下是无法释放的,这点需要注意。

前面一节主要描述了 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 宏判断。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值