Symbian OS编码诀窍之编码诀窍

本文提供了Symbian OS编程的关键建议,包括应用程序设计、内存管理、异常处理、ActiveObjects使用等,帮助开发者避免常见错误,提高程序稳定性和性能。
  
     确保您的应用程序能够对系统关机事件做出响应。在您的AppUi::HandleCommandL()方法中,必须要对EEikCmdExit(以及任何特定平台相关的事件,例如Series 60上的EAknSoftkeyBack)做出响应。
     要对外来系统事件做出响应。请牢记,您的应用程序在一个多任务电话系统上运行,您需要将注意力集中于刚获得/丢失的事件上,以确保当用户获得一个高优先级的通知时您能够做出正确响应。例如,正打进来的电话会干扰您的应用程序的运行,这时应确保您已保存了系统状态和数据(即:您需要对标准的“背景”事件采取恰当的行动—请参阅SDK)。一般来说,系统框架会处理这个问题,您不需要采取任何特殊行动—但一定要确保您没有妨碍系统框架的正常操作。
     内存处理是Symbian OS需要考虑的一个重要课题。在这一点上,应注意电话有时会不同于模拟器。因此在将您的应用程序呈交给“Symbian 认证签名”进行测试之前,务必确保已经在实际电话设备上测试了您的程序。
     内存堆栈空间有限!应尽可能将对象放到内存堆中,而不要放到栈里。KERN-EXEC 3异常(panic)发生的主要原因之一就是栈的破坏/溢出。
     应用程序发生异常(panic)表明您的代码中一定有错误。以下是一些主要的、常见的错误:
Ⅰ:忘记将非对象成员、被分配到堆的变量加到CleanupStack上。
 Ⅱ:将成员变量放到CleanupStack上—这一点要千万避免(在析构函数中将这些变量删除就可以了)。
 Ⅲ:“重复删除”--例如,没有正确的从CleanupStack上Pop()出已经被销毁的对象,造成CleanupStack以后试图再次删除它。或者使用过一个对象之后将其删除但忘记将其值设成NULL,从而在析构函数里又试图删除一次。
  Ⅳ:用可能不存在于您的析构函数中的变量调用函数。例如,以下代码可能导致异常因为有可能您在分配内存之前您的对象已经被销毁,或者在应用程序的另一处已经删除了该内存,这样iSomeServer就会处于NULL:
     CMyClass::~CMyClass()
{
iSomeServer->Close();
delete iSomeServer;
}
应该如下编写该代码:
CMyClass::~CMyClass()
{
If(iSomeServer)
{
iSomeServer->Close();
delete iSomeServer;
}
}
  Ⅴ:在NULL指针上调用函数。
Ⅵ:函数调用另一个函数,而其使用的变量已经超出范畴,例如:
    把一个栈变量传送到一个异步函数的回调(callback)里。
     在系统资源不够的情况下,得体的处理失效情况是非常重要的。最受限制的资源通常是系统RAM,因此您需要注意正确的处理内存不足的情况。采用“两端构造方法”和如下所述的CleanupStack机制,对这种防御性编程来说是必不可少和极其重要的。
     对带“R”字头、具备Close()方法的类,总是使用CleanupClosePushL()。这将确保当Leave事件发生时,它们会被恰当的清除。例如:
RFile file;
User::LeaveIfError(file.Open(…));
CleanupClosePushL(file);
CleanupStack::PopAndDestroy(&file);
对用Release()或Destroy()的“R”类,亦可使用CleanupDeletePushL()及CleanupRelasePushL()来取代Close()。
     另外,请记住CleanupStack机制是可扩展的,面对所有Leave事件,都可以用它来有效的清除任何对象。即使您需要处理的是较复杂的情况,也不应该忽略采用正规的清理机制。欲进一步了解TCleanupItem,请参阅Symbian OS Library文档。
     倘若您意图对HBufC变量重新分配资源,在清除它们之后,总是将其设为NULL。由于HBufC的资源分配或其再分配可能会导致Leave事件的发生,从而可能会出现析构函数试图删除已经不存在的HBufC变量的情况。当然,对于任何由堆分配资源的变量而言都应如此,对HBufC变量采取此做法更是已经成为普遍的使用模式。
     当必须采用自己的TRAP时,请忽略所有的报错。常见的编码错误是:
TRAPD(err, DoSomethingL());
If(err == KErrNone || err == KErrNotFound)
{
//Do something else
}
这意味着其他错误码都被忽略。然而,倘若您非用上述模式不可,应采用Leave机制来处理其他错误:
TRAPD(err, DoSomethingL());
If(err == KErrNone||err == KerrNotFound)
{
//Do something else
}
else
User::Leave(err);
 
不要延迟将对象PushL()到CleanupStack上。所有新创建的对象(成员变量除外)应被立即压入该堆栈。例如,下面的做法是错的:
Void doExampleL()
{
CSomeObject* myObject1 = new (ELeave)CSomeObject;
CSomeObject* myObject2 = new (ELeave)CSomeObject;
//Do something here with the variables
CleanupStack::PushL(myObject1);
CleanupStack::PushL(myObject2);
//Do something more with the variables
CleanupStack::PopAndDestroy(2);
//myObject2,myObject1
}
因为myObject2的创建可能失败,造成myObject1”悬”在那里不能被清理。应该这样来实现:
Void doExampleL()
{
CSomeObject* myObject1 = new (ELeave)CSomeObject;
CleanupStack::PushL(myObject1);
CSomeObject* myObject2 = new (ELeave)CSomeObject;
CleanupStack::PushL(myObject2);
//Do something here with the variables
CleanupStack::PopAndDestroy(2);
//myObject2,myObject1
}
注意,那些名称有大写字母C结尾的函数(例如NewLC())会自动把其对象置于CleanupStack。您不应该自己来将这些对象压入CleanupStack,否则该对象会入栈两次。当您创建非成员变量并为其分配内存时,这些由C结尾的函数很有用。
“两端构造方法”是Symbian OS内存管理的关键部分。基本原则是Symbian OS中的构造函数或析构函数永远不应该发生Leave。倘若一个C++构造函数Leave,构造过程未完成的对象得不到清理,因为还没有生成指针指向该对象。为此,Symbian OS中的构造函数仅将该对象实例化,而后调用该对象的ConstructL()函数,在其中将成员数据实例化。一旦ConstructL()发生Leave,标准的析构函数将被调用来清除所有至此已被成功分配的成员变量。在您的编码中照用这一设计模式来防止内存泄漏,至为关键。当您写每一行代码时,都应该问自己:“这一行代码能否发生Leave?”假如回答为“是”,则应考虑“是否所有资源都将被释放?”。
编码中请勿使用_L()宏---而应使用_LIT()。_L()自Symbian OS V5起已是“不推荐使用”(deprecated),它的问题在于它将调用TPtrC(const TText*)构造函数,该构造函数会调用strlen()函数来计算该字串的长度。虽然这不会带来额外的RAM开销,却会在运行时占用更多CPU周期。相反,宏_LIT()直接创建了一个在编译时就全部实例化的对象,节省了构造TPtrC的CPU开销。当然,您首先应该考虑的是否应该使用硬编码的字符串常量,因为当您将来地方化(localize)您的程序时,这种常量类型的描述符(descriptor)可能需要重新编码(我觉得作者这里写的有问题,_LIT()及_L()定义的是常量字符串,并非描述符,它不能够用描述符的一些通用的方法,只有调用_LIT()和_L()重载的()运算符才会变成const TDesC&类型的描述符,并使TDesC的一些通用方法可用---孙东风注)
当在函数参数中使用描述符(descriptor)时,应缺省使用基类。在大多数情况下,以const TDesC&形式来传递描述符。对可修改的描述符,则应使用TDes&。
当在函数中传递或返回对象时,应确保如果您拥有该对象的所有权,您应负责将其清除!Symbian采取的约定是:函数中的指针表示所有权转移到调用者,而使用引用则表示被传递对象的所有权仍属于原所有者。
Active Objects是Symbian OS的重要特性之一。请仔细研究SDK文档、Symbian Developer NetWork白皮书,以充分理解其工作原理。下面有一些有用的窍门:
Ⅰ:在RunL()内无需使用TRAP()。Active Scheduler本身会TRAP函数RunL()并在其发Leave时调用CActive::RunError()。
Ⅱ:为此,您应实现自己的RunError()函数来处理从RunL()的Leave事件。
Ⅲ:保证RunL()操作尽可能简短。长时间运行的RunL()将阻塞其他Active Objects。
Ⅳ:总是实现DoCancel()函数,总是在AO析构函数中调用Cancel()。
您应尽可能利用Active Object框架机制。对于使用电池供电的设备,在一个循环中紧密不断地进行轮流检测(polling)是极其不适当的,将带来大量耗电。写游戏时,对此尤需特别注意,详情参阅Symbian Developer Network网站的技术文档:
www.symbian.com/developer/techlib/papers/porting_3D_games/XenGames_paper.pdf
ViewSrv 11异常对于繁忙运行的程序(例如游戏)是一个潜在的问题。当您的,或者其他任何程序中的ViewSrv active object不能及时响应View Server时就会导致此种异常。典型的最长回应时间是10-20秒。FAQ-0900有详细解释,FAQ-0920有针对如何避免此类问题的实用技巧。二者均可从www3.symbian.com/faq.nsf网页上的Symbian OS FAQ数据库获取。
您无需使用HBufC::Des()来进入一个HBufC对象。只需采用*操作符来为HBufC对象解除引用(dereference)。这对于向某个接受TDesC&(上文的推荐做法)的函数传递HBufC参数时尤其有用。
21当使用标准的程序.INI文件的功能时(即在您的应用UI类中使用Application()->OpenIniFileLC();API时),确保将版本号信息写入流(stream)中。这样使您能够在未来新版本的程序中建立新的流,意味着即使某个最终用户将来安装您的软件的新版本时,不会因为在旧的.INI文件中找不到正确配置或流时发生异常。
22.在您的程序中实现框架类(framework class)时要小心。应该始终从所提供的平台相关的框架类中继承。例如,对UIQ而言,不要从CQikAppUi继承。所有的应用基类(CQikAppUi、CQikApplication、CQikDocument)添加的功能支持更广的框架范围来保证应用程序正确运行。



  
     确保您的应用程序能够对系统关机事件做出响应。在您的AppUi::HandleCommandL()方法中,必须要对EEikCmdExit(以及任何特定平台相关的事件,例如Series 60上的EAknSoftkeyBack)做出响应。
     要对外来系统事件做出响应。请牢记,您的应用程序在一个多任务电话系统上运行,您需要将注意力集中于刚获得/丢失的事件上,以确保当用户获得一个高优先级的通知时您能够做出正确响应。例如,正打进来的电话会干扰您的应用程序的运行,这时应确保您已保存了系统状态和数据(即:您需要对标准的“背景”事件采取恰当的行动—请参阅SDK)。一般来说,系统框架会处理这个问题,您不需要采取任何特殊行动—但一定要确保您没有妨碍系统框架的正常操作。
     内存处理是Symbian OS需要考虑的一个重要课题。在这一点上,应注意电话有时会不同于模拟器。因此在将您的应用程序呈交给“Symbian 认证签名”进行测试之前,务必确保已经在实际电话设备上测试了您的程序。
     内存堆栈空间有限!应尽可能将对象放到内存堆中,而不要放到栈里。KERN-EXEC 3异常(panic)发生的主要原因之一就是栈的破坏/溢出。
     应用程序发生异常(panic)表明您的代码中一定有错误。以下是一些主要的、常见的错误:
Ⅰ:忘记将非对象成员、被分配到堆的变量加到CleanupStack上。
 Ⅱ:将成员变量放到CleanupStack上—这一点要千万避免(在析构函数中将这些变量删除就可以了)。
 Ⅲ:“重复删除”--例如,没有正确的从CleanupStack上Pop()出已经被销毁的对象,造成CleanupStack以后试图再次删除它。或者使用过一个对象之后将其删除但忘记将其值设成NULL,从而在析构函数里又试图删除一次。
  Ⅳ:用可能不存在于您的析构函数中的变量调用函数。例如,以下代码可能导致异常因为有可能您在分配内存之前您的对象已经被销毁,或者在应用程序的另一处已经删除了该内存,这样iSomeServer就会处于NULL:
     CMyClass::~CMyClass()
{
iSomeServer->Close();
delete iSomeServer;
}
应该如下编写该代码:
CMyClass::~CMyClass()
{
If(iSomeServer)
{
iSomeServer->Close();
delete iSomeServer;
}
}
  Ⅴ:在NULL指针上调用函数。
Ⅵ:函数调用另一个函数,而其使用的变量已经超出范畴,例如:
    把一个栈变量传送到一个异步函数的回调(callback)里。
     在系统资源不够的情况下,得体的处理失效情况是非常重要的。最受限制的资源通常是系统RAM,因此您需要注意正确的处理内存不足的情况。采用“两端构造方法”和如下所述的CleanupStack机制,对这种防御性编程来说是必不可少和极其重要的。
     对带“R”字头、具备Close()方法的类,总是使用CleanupClosePushL()。这将确保当Leave事件发生时,它们会被恰当的清除。例如:
RFile file;
User::LeaveIfError(file.Open(…));
CleanupClosePushL(file);
CleanupStack::PopAndDestroy(&file);
对用Release()或Destroy()的“R”类,亦可使用CleanupDeletePushL()及CleanupRelasePushL()来取代Close()。
     另外,请记住CleanupStack机制是可扩展的,面对所有Leave事件,都可以用它来有效的清除任何对象。即使您需要处理的是较复杂的情况,也不应该忽略采用正规的清理机制。欲进一步了解TCleanupItem,请参阅Symbian OS Library文档。
     倘若您意图对HBufC变量重新分配资源,在清除它们之后,总是将其设为NULL。由于HBufC的资源分配或其再分配可能会导致Leave事件的发生,从而可能会出现析构函数试图删除已经不存在的HBufC变量的情况。当然,对于任何由堆分配资源的变量而言都应如此,对HBufC变量采取此做法更是已经成为普遍的使用模式。
     当必须采用自己的TRAP时,请忽略所有的报错。常见的编码错误是:
TRAPD(err, DoSomethingL());
If(err == KErrNone || err == KErrNotFound)
{
//Do something else
}
这意味着其他错误码都被忽略。然而,倘若您非用上述模式不可,应采用Leave机制来处理其他错误:
TRAPD(err, DoSomethingL());
If(err == KErrNone||err == KerrNotFound)
{
//Do something else
}
else
User::Leave(err);
 
不要延迟将对象PushL()到CleanupStack上。所有新创建的对象(成员变量除外)应被立即压入该堆栈。例如,下面的做法是错的:
Void doExampleL()
{
CSomeObject* myObject1 = new (ELeave)CSomeObject;
CSomeObject* myObject2 = new (ELeave)CSomeObject;
//Do something here with the variables
CleanupStack::PushL(myObject1);
CleanupStack::PushL(myObject2);
//Do something more with the variables
CleanupStack::PopAndDestroy(2);
//myObject2,myObject1
}
因为myObject2的创建可能失败,造成myObject1”悬”在那里不能被清理。应该这样来实现:
Void doExampleL()
{
CSomeObject* myObject1 = new (ELeave)CSomeObject;
CleanupStack::PushL(myObject1);
CSomeObject* myObject2 = new (ELeave)CSomeObject;
CleanupStack::PushL(myObject2);
//Do something here with the variables
CleanupStack::PopAndDestroy(2);
//myObject2,myObject1
}
注意,那些名称有大写字母C结尾的函数(例如NewLC())会自动把其对象置于CleanupStack。您不应该自己来将这些对象压入CleanupStack,否则该对象会入栈两次。当您创建非成员变量并为其分配内存时,这些由C结尾的函数很有用。
“两端构造方法”是Symbian OS内存管理的关键部分。基本原则是Symbian OS中的构造函数或析构函数永远不应该发生Leave。倘若一个C++构造函数Leave,构造过程未完成的对象得不到清理,因为还没有生成指针指向该对象。为此,Symbian OS中的构造函数仅将该对象实例化,而后调用该对象的ConstructL()函数,在其中将成员数据实例化。一旦ConstructL()发生Leave,标准的析构函数将被调用来清除所有至此已被成功分配的成员变量。在您的编码中照用这一设计模式来防止内存泄漏,至为关键。当您写每一行代码时,都应该问自己:“这一行代码能否发生Leave?”假如回答为“是”,则应考虑“是否所有资源都将被释放?”。
编码中请勿使用_L()宏---而应使用_LIT()。_L()自Symbian OS V5起已是“不推荐使用”(deprecated),它的问题在于它将调用TPtrC(const TText*)构造函数,该构造函数会调用strlen()函数来计算该字串的长度。虽然这不会带来额外的RAM开销,却会在运行时占用更多CPU周期。相反,宏_LIT()直接创建了一个在编译时就全部实例化的对象,节省了构造TPtrC的CPU开销。当然,您首先应该考虑的是否应该使用硬编码的字符串常量,因为当您将来地方化(localize)您的程序时,这种常量类型的描述符(descriptor)可能需要重新编码(我觉得作者这里写的有问题,_LIT()及_L()定义的是常量字符串,并非描述符,它不能够用描述符的一些通用的方法,只有调用_LIT()和_L()重载的()运算符才会变成const TDesC&类型的描述符,并使TDesC的一些通用方法可用---孙东风注)
当在函数参数中使用描述符(descriptor)时,应缺省使用基类。在大多数情况下,以const TDesC&形式来传递描述符。对可修改的描述符,则应使用TDes&。
当在函数中传递或返回对象时,应确保如果您拥有该对象的所有权,您应负责将其清除!Symbian采取的约定是:函数中的指针表示所有权转移到调用者,而使用引用则表示被传递对象的所有权仍属于原所有者。
Active Objects是Symbian OS的重要特性之一。请仔细研究SDK文档、Symbian Developer NetWork白皮书,以充分理解其工作原理。下面有一些有用的窍门:
Ⅰ:在RunL()内无需使用TRAP()。Active Scheduler本身会TRAP函数RunL()并在其发Leave时调用CActive::RunError()。
Ⅱ:为此,您应实现自己的RunError()函数来处理从RunL()的Leave事件。
Ⅲ:保证RunL()操作尽可能简短。长时间运行的RunL()将阻塞其他Active Objects。
Ⅳ:总是实现DoCancel()函数,总是在AO析构函数中调用Cancel()。
您应尽可能利用Active Object框架机制。对于使用电池供电的设备,在一个循环中紧密不断地进行轮流检测(polling)是极其不适当的,将带来大量耗电。写游戏时,对此尤需特别注意,详情参阅Symbian Developer Network网站的技术文档:
www.symbian.com/developer/techlib/papers/porting_3D_games/XenGames_paper.pdf
ViewSrv 11异常对于繁忙运行的程序(例如游戏)是一个潜在的问题。当您的,或者其他任何程序中的ViewSrv active object不能及时响应View Server时就会导致此种异常。典型的最长回应时间是10-20秒。FAQ-0900有详细解释,FAQ-0920有针对如何避免此类问题的实用技巧。二者均可从www3.symbian.com/faq.nsf网页上的Symbian OS FAQ数据库获取。
您无需使用HBufC::Des()来进入一个HBufC对象。只需采用*操作符来为HBufC对象解除引用(dereference)。这对于向某个接受TDesC&(上文的推荐做法)的函数传递HBufC参数时尤其有用。
21当使用标准的程序.INI文件的功能时(即在您的应用UI类中使用Application()->OpenIniFileLC();API时),确保将版本号信息写入流(stream)中。这样使您能够在未来新版本的程序中建立新的流,意味着即使某个最终用户将来安装您的软件的新版本时,不会因为在旧的.INI文件中找不到正确配置或流时发生异常。
22.在您的程序中实现框架类(framework class)时要小心。应该始终从所提供的平台相关的框架类中继承。例如,对UIQ而言,不要从CQikAppUi继承。所有的应用基类(CQikAppUi、CQikApplication、CQikDocument)添加的功能支持更广的框架范围来保证应用程序正确运行。



  
     确保您的应用程序能够对系统关机事件做出响应。在您的AppUi::HandleCommandL()方法中,必须要对EEikCmdExit(以及任何特定平台相关的事件,例如Series 60上的EAknSoftkeyBack)做出响应。
     要对外来系统事件做出响应。请牢记,您的应用程序在一个多任务电话系统上运行,您需要将注意力集中于刚获得/丢失的事件上,以确保当用户获得一个高优先级的通知时您能够做出正确响应。例如,正打进来的电话会干扰您的应用程序的运行,这时应确保您已保存了系统状态和数据(即:您需要对标准的“背景”事件采取恰当的行动—请参阅SDK)。一般来说,系统框架会处理这个问题,您不需要采取任何特殊行动—但一定要确保您没有妨碍系统框架的正常操作。
     内存处理是Symbian OS需要考虑的一个重要课题。在这一点上,应注意电话有时会不同于模拟器。因此在将您的应用程序呈交给“Symbian 认证签名”进行测试之前,务必确保已经在实际电话设备上测试了您的程序。
     内存堆栈空间有限!应尽可能将对象放到内存堆中,而不要放到栈里。KERN-EXEC 3异常(panic)发生的主要原因之一就是栈的破坏/溢出。
     应用程序发生异常(panic)表明您的代码中一定有错误。以下是一些主要的、常见的错误:
Ⅰ:忘记将非对象成员、被分配到堆的变量加到CleanupStack上。
 Ⅱ:将成员变量放到CleanupStack上—这一点要千万避免(在析构函数中将这些变量删除就可以了)。
 Ⅲ:“重复删除”--例如,没有正确的从CleanupStack上Pop()出已经被销毁的对象,造成CleanupStack以后试图再次删除它。或者使用过一个对象之后将其删除但忘记将其值设成NULL,从而在析构函数里又试图删除一次。
  Ⅳ:用可能不存在于您的析构函数中的变量调用函数。例如,以下代码可能导致异常因为有可能您在分配内存之前您的对象已经被销毁,或者在应用程序的另一处已经删除了该内存,这样iSomeServer就会处于NULL:
     CMyClass::~CMyClass()
{
iSomeServer->Close();
delete iSomeServer;
}
应该如下编写该代码:
CMyClass::~CMyClass()
{
If(iSomeServer)
{
iSomeServer->Close();
delete iSomeServer;
}
}
  Ⅴ:在NULL指针上调用函数。
Ⅵ:函数调用另一个函数,而其使用的变量已经超出范畴,例如:
    把一个栈变量传送到一个异步函数的回调(callback)里。
     在系统资源不够的情况下,得体的处理失效情况是非常重要的。最受限制的资源通常是系统RAM,因此您需要注意正确的处理内存不足的情况。采用“两端构造方法”和如下所述的CleanupStack机制,对这种防御性编程来说是必不可少和极其重要的。
     对带“R”字头、具备Close()方法的类,总是使用CleanupClosePushL()。这将确保当Leave事件发生时,它们会被恰当的清除。例如:
RFile file;
User::LeaveIfError(file.Open(…));
CleanupClosePushL(file);
CleanupStack::PopAndDestroy(&file);
对用Release()或Destroy()的“R”类,亦可使用CleanupDeletePushL()及CleanupRelasePushL()来取代Close()。
     另外,请记住CleanupStack机制是可扩展的,面对所有Leave事件,都可以用它来有效的清除任何对象。即使您需要处理的是较复杂的情况,也不应该忽略采用正规的清理机制。欲进一步了解TCleanupItem,请参阅Symbian OS Library文档。
     倘若您意图对HBufC变量重新分配资源,在清除它们之后,总是将其设为NULL。由于HBufC的资源分配或其再分配可能会导致Leave事件的发生,从而可能会出现析构函数试图删除已经不存在的HBufC变量的情况。当然,对于任何由堆分配资源的变量而言都应如此,对HBufC变量采取此做法更是已经成为普遍的使用模式。
     当必须采用自己的TRAP时,请忽略所有的报错。常见的编码错误是:
TRAPD(err, DoSomethingL());
If(err == KErrNone || err == KErrNotFound)
{
//Do something else
}
这意味着其他错误码都被忽略。然而,倘若您非用上述模式不可,应采用Leave机制来处理其他错误:
TRAPD(err, DoSomethingL());
If(err == KErrNone||err == KerrNotFound)
{
//Do something else
}
else
User::Leave(err);
 
不要延迟将对象PushL()到CleanupStack上。所有新创建的对象(成员变量除外)应被立即压入该堆栈。例如,下面的做法是错的:
Void doExampleL()
{
CSomeObject* myObject1 = new (ELeave)CSomeObject;
CSomeObject* myObject2 = new (ELeave)CSomeObject;
//Do something here with the variables
CleanupStack::PushL(myObject1);
CleanupStack::PushL(myObject2);
//Do something more with the variables
CleanupStack::PopAndDestroy(2);
//myObject2,myObject1
}
因为myObject2的创建可能失败,造成myObject1”悬”在那里不能被清理。应该这样来实现:
Void doExampleL()
{
CSomeObject* myObject1 = new (ELeave)CSomeObject;
CleanupStack::PushL(myObject1);
CSomeObject* myObject2 = new (ELeave)CSomeObject;
CleanupStack::PushL(myObject2);
//Do something here with the variables
CleanupStack::PopAndDestroy(2);
//myObject2,myObject1
}
注意,那些名称有大写字母C结尾的函数(例如NewLC())会自动把其对象置于CleanupStack。您不应该自己来将这些对象压入CleanupStack,否则该对象会入栈两次。当您创建非成员变量并为其分配内存时,这些由C结尾的函数很有用。
“两端构造方法”是Symbian OS内存管理的关键部分。基本原则是Symbian OS中的构造函数或析构函数永远不应该发生Leave。倘若一个C++构造函数Leave,构造过程未完成的对象得不到清理,因为还没有生成指针指向该对象。为此,Symbian OS中的构造函数仅将该对象实例化,而后调用该对象的ConstructL()函数,在其中将成员数据实例化。一旦ConstructL()发生Leave,标准的析构函数将被调用来清除所有至此已被成功分配的成员变量。在您的编码中照用这一设计模式来防止内存泄漏,至为关键。当您写每一行代码时,都应该问自己:“这一行代码能否发生Leave?”假如回答为“是”,则应考虑“是否所有资源都将被释放?”。
编码中请勿使用_L()宏---而应使用_LIT()。_L()自Symbian OS V5起已是“不推荐使用”(deprecated),它的问题在于它将调用TPtrC(const TText*)构造函数,该构造函数会调用strlen()函数来计算该字串的长度。虽然这不会带来额外的RAM开销,却会在运行时占用更多CPU周期。相反,宏_LIT()直接创建了一个在编译时就全部实例化的对象,节省了构造TPtrC的CPU开销。当然,您首先应该考虑的是否应该使用硬编码的字符串常量,因为当您将来地方化(localize)您的程序时,这种常量类型的描述符(descriptor)可能需要重新编码(我觉得作者这里写的有问题,_LIT()及_L()定义的是常量字符串,并非描述符,它不能够用描述符的一些通用的方法,只有调用_LIT()和_L()重载的()运算符才会变成const TDesC&类型的描述符,并使TDesC的一些通用方法可用---孙东风注)
当在函数参数中使用描述符(descriptor)时,应缺省使用基类。在大多数情况下,以const TDesC&形式来传递描述符。对可修改的描述符,则应使用TDes&。
当在函数中传递或返回对象时,应确保如果您拥有该对象的所有权,您应负责将其清除!Symbian采取的约定是:函数中的指针表示所有权转移到调用者,而使用引用则表示被传递对象的所有权仍属于原所有者。
Active Objects是Symbian OS的重要特性之一。请仔细研究SDK文档、Symbian Developer NetWork白皮书,以充分理解其工作原理。下面有一些有用的窍门:
Ⅰ:在RunL()内无需使用TRAP()。Active Scheduler本身会TRAP函数RunL()并在其发Leave时调用CActive::RunError()。
Ⅱ:为此,您应实现自己的RunError()函数来处理从RunL()的Leave事件。
Ⅲ:保证RunL()操作尽可能简短。长时间运行的RunL()将阻塞其他Active Objects。
Ⅳ:总是实现DoCancel()函数,总是在AO析构函数中调用Cancel()。
您应尽可能利用Active Object框架机制。对于使用电池供电的设备,在一个循环中紧密不断地进行轮流检测(polling)是极其不适当的,将带来大量耗电。写游戏时,对此尤需特别注意,详情参阅Symbian Developer Network网站的技术文档:
www.symbian.com/developer/techlib/papers/porting_3D_games/XenGames_paper.pdf
ViewSrv 11异常对于繁忙运行的程序(例如游戏)是一个潜在的问题。当您的,或者其他任何程序中的ViewSrv active object不能及时响应View Server时就会导致此种异常。典型的最长回应时间是10-20秒。FAQ-0900有详细解释,FAQ-0920有针对如何避免此类问题的实用技巧。二者均可从www3.symbian.com/faq.nsf网页上的Symbian OS FAQ数据库获取。
您无需使用HBufC::Des()来进入一个HBufC对象。只需采用*操作符来为HBufC对象解除引用(dereference)。这对于向某个接受TDesC&(上文的推荐做法)的函数传递HBufC参数时尤其有用。
21当使用标准的程序.INI文件的功能时(即在您的应用UI类中使用Application()->OpenIniFileLC();API时),确保将版本号信息写入流(stream)中。这样使您能够在未来新版本的程序中建立新的流,意味着即使某个最终用户将来安装您的软件的新版本时,不会因为在旧的.INI文件中找不到正确配置或流时发生异常。
22.在您的程序中实现框架类(framework class)时要小心。应该始终从所提供的平台相关的框架类中继承。例如,对UIQ而言,不要从CQikAppUi继承。所有的应用基类(CQikAppUi、CQikApplication、CQikDocument)添加的功能支持更广的框架范围来保证应用程序正确运行。



  
     确保您的应用程序能够对系统关机事件做出响应。在您的AppUi::HandleCommandL()方法中,必须要对EEikCmdExit(以及任何特定平台相关的事件,例如Series 60上的EAknSoftkeyBack)做出响应。
     要对外来系统事件做出响应。请牢记,您的应用程序在一个多任务电话系统上运行,您需要将注意力集中于刚获得/丢失的事件上,以确保当用户获得一个高优先级的通知时您能够做出正确响应。例如,正打进来的电话会干扰您的应用程序的运行,这时应确保您已保存了系统状态和数据(即:您需要对标准的“背景”事件采取恰当的行动—请参阅SDK)。一般来说,系统框架会处理这个问题,您不需要采取任何特殊行动—但一定要确保您没有妨碍系统框架的正常操作。
     内存处理是Symbian OS需要考虑的一个重要课题。在这一点上,应注意电话有时会不同于模拟器。因此在将您的应用程序呈交给“Symbian 认证签名”进行测试之前,务必确保已经在实际电话设备上测试了您的程序。
     内存堆栈空间有限!应尽可能将对象放到内存堆中,而不要放到栈里。KERN-EXEC 3异常(panic)发生的主要原因之一就是栈的破坏/溢出。
     应用程序发生异常(panic)表明您的代码中一定有错误。以下是一些主要的、常见的错误:
Ⅰ:忘记将非对象成员、被分配到堆的变量加到CleanupStack上。
 Ⅱ:将成员变量放到CleanupStack上—这一点要千万避免(在析构函数中将这些变量删除就可以了)。
 Ⅲ:“重复删除”--例如,没有正确的从CleanupStack上Pop()出已经被销毁的对象,造成CleanupStack以后试图再次删除它。或者使用过一个对象之后将其删除但忘记将其值设成NULL,从而在析构函数里又试图删除一次。
  Ⅳ:用可能不存在于您的析构函数中的变量调用函数。例如,以下代码可能导致异常因为有可能您在分配内存之前您的对象已经被销毁,或者在应用程序的另一处已经删除了该内存,这样iSomeServer就会处于NULL:
     CMyClass::~CMyClass()
{
iSomeServer->Close();
delete iSomeServer;
}
应该如下编写该代码:
CMyClass::~CMyClass()
{
If(iSomeServer)
{
iSomeServer->Close();
delete iSomeServer;
}
}
  Ⅴ:在NULL指针上调用函数。
Ⅵ:函数调用另一个函数,而其使用的变量已经超出范畴,例如:
    把一个栈变量传送到一个异步函数的回调(callback)里。
     在系统资源不够的情况下,得体的处理失效情况是非常重要的。最受限制的资源通常是系统RAM,因此您需要注意正确的处理内存不足的情况。采用“两端构造方法”和如下所述的CleanupStack机制,对这种防御性编程来说是必不可少和极其重要的。
     对带“R”字头、具备Close()方法的类,总是使用CleanupClosePushL()。这将确保当Leave事件发生时,它们会被恰当的清除。例如:
RFile file;
User::LeaveIfError(file.Open(…));
CleanupClosePushL(file);
CleanupStack::PopAndDestroy(&file);
对用Release()或Destroy()的“R”类,亦可使用CleanupDeletePushL()及CleanupRelasePushL()来取代Close()。
     另外,请记住CleanupStack机制是可扩展的,面对所有Leave事件,都可以用它来有效的清除任何对象。即使您需要处理的是较复杂的情况,也不应该忽略采用正规的清理机制。欲进一步了解TCleanupItem,请参阅Symbian OS Library文档。
     倘若您意图对HBufC变量重新分配资源,在清除它们之后,总是将其设为NULL。由于HBufC的资源分配或其再分配可能会导致Leave事件的发生,从而可能会出现析构函数试图删除已经不存在的HBufC变量的情况。当然,对于任何由堆分配资源的变量而言都应如此,对HBufC变量采取此做法更是已经成为普遍的使用模式。
     当必须采用自己的TRAP时,请忽略所有的报错。常见的编码错误是:
TRAPD(err, DoSomethingL());
If(err == KErrNone || err == KErrNotFound)
{
//Do something else
}
这意味着其他错误码都被忽略。然而,倘若您非用上述模式不可,应采用Leave机制来处理其他错误:
TRAPD(err, DoSomethingL());
If(err == KErrNone||err == KerrNotFound)
{
//Do something else
}
else
User::Leave(err);
 
不要延迟将对象PushL()到CleanupStack上。所有新创建的对象(成员变量除外)应被立即压入该堆栈。例如,下面的做法是错的:
Void doExampleL()
{
CSomeObject* myObject1 = new (ELeave)CSomeObject;
CSomeObject* myObject2 = new (ELeave)CSomeObject;
//Do something here with the variables
CleanupStack::PushL(myObject1);
CleanupStack::PushL(myObject2);
//Do something more with the variables
CleanupStack::PopAndDestroy(2);
//myObject2,myObject1
}
因为myObject2的创建可能失败,造成myObject1”悬”在那里不能被清理。应该这样来实现:
Void doExampleL()
{
CSomeObject* myObject1 = new (ELeave)CSomeObject;
CleanupStack::PushL(myObject1);
CSomeObject* myObject2 = new (ELeave)CSomeObject;
CleanupStack::PushL(myObject2);
//Do something here with the variables
CleanupStack::PopAndDestroy(2);
//myObject2,myObject1
}
注意,那些名称有大写字母C结尾的函数(例如NewLC())会自动把其对象置于CleanupStack。您不应该自己来将这些对象压入CleanupStack,否则该对象会入栈两次。当您创建非成员变量并为其分配内存时,这些由C结尾的函数很有用。
“两端构造方法”是Symbian OS内存管理的关键部分。基本原则是Symbian OS中的构造函数或析构函数永远不应该发生Leave。倘若一个C++构造函数Leave,构造过程未完成的对象得不到清理,因为还没有生成指针指向该对象。为此,Symbian OS中的构造函数仅将该对象实例化,而后调用该对象的ConstructL()函数,在其中将成员数据实例化。一旦ConstructL()发生Leave,标准的析构函数将被调用来清除所有至此已被成功分配的成员变量。在您的编码中照用这一设计模式来防止内存泄漏,至为关键。当您写每一行代码时,都应该问自己:“这一行代码能否发生Leave?”假如回答为“是”,则应考虑“是否所有资源都将被释放?”。
编码中请勿使用_L()宏---而应使用_LIT()。_L()自Symbian OS V5起已是“不推荐使用”(deprecated),它的问题在于它将调用TPtrC(const TText*)构造函数,该构造函数会调用strlen()函数来计算该字串的长度。虽然这不会带来额外的RAM开销,却会在运行时占用更多CPU周期。相反,宏_LIT()直接创建了一个在编译时就全部实例化的对象,节省了构造TPtrC的CPU开销。当然,您首先应该考虑的是否应该使用硬编码的字符串常量,因为当您将来地方化(localize)您的程序时,这种常量类型的描述符(descriptor)可能需要重新编码(我觉得作者这里写的有问题,_LIT()及_L()定义的是常量字符串,并非描述符,它不能够用描述符的一些通用的方法,只有调用_LIT()和_L()重载的()运算符才会变成const TDesC&类型的描述符,并使TDesC的一些通用方法可用---孙东风注)
当在函数参数中使用描述符(descriptor)时,应缺省使用基类。在大多数情况下,以const TDesC&形式来传递描述符。对可修改的描述符,则应使用TDes&。
当在函数中传递或返回对象时,应确保如果您拥有该对象的所有权,您应负责将其清除!Symbian采取的约定是:函数中的指针表示所有权转移到调用者,而使用引用则表示被传递对象的所有权仍属于原所有者。
Active Objects是Symbian OS的重要特性之一。请仔细研究SDK文档、Symbian Developer NetWork白皮书,以充分理解其工作原理。下面有一些有用的窍门:
Ⅰ:在RunL()内无需使用TRAP()。Active Scheduler本身会TRAP函数RunL()并在其发Leave时调用CActive::RunError()。
Ⅱ:为此,您应实现自己的RunError()函数来处理从RunL()的Leave事件。
Ⅲ:保证RunL()操作尽可能简短。长时间运行的RunL()将阻塞其他Active Objects。
Ⅳ:总是实现DoCancel()函数,总是在AO析构函数中调用Cancel()。
您应尽可能利用Active Object框架机制。对于使用电池供电的设备,在一个循环中紧密不断地进行轮流检测(polling)是极其不适当的,将带来大量耗电。写游戏时,对此尤需特别注意,详情参阅Symbian Developer Network网站的技术文档:
www.symbian.com/developer/techlib/papers/porting_3D_games/XenGames_paper.pdf
ViewSrv 11异常对于繁忙运行的程序(例如游戏)是一个潜在的问题。当您的,或者其他任何程序中的ViewSrv active object不能及时响应View Server时就会导致此种异常。典型的最长回应时间是10-20秒。FAQ-0900有详细解释,FAQ-0920有针对如何避免此类问题的实用技巧。二者均可从www3.symbian.com/faq.nsf网页上的Symbian OS FAQ数据库获取。
您无需使用HBufC::Des()来进入一个HBufC对象。只需采用*操作符来为HBufC对象解除引用(dereference)。这对于向某个接受TDesC&(上文的推荐做法)的函数传递HBufC参数时尤其有用。
21当使用标准的程序.INI文件的功能时(即在您的应用UI类中使用Application()->OpenIniFileLC();API时),确保将版本号信息写入流(stream)中。这样使您能够在未来新版本的程序中建立新的流,意味着即使某个最终用户将来安装您的软件的新版本时,不会因为在旧的.INI文件中找不到正确配置或流时发生异常。
22.在您的程序中实现框架类(framework class)时要小心。应该始终从所提供的平台相关的框架类中继承。例如,对UIQ而言,不要从CQikAppUi继承。所有的应用基类(CQikAppUi、CQikApplication、CQikDocument)添加的功能支持更广的框架范围来保证应用程序正确运行。



  
     确保您的应用程序能够对系统关机事件做出响应。在您的AppUi::HandleCommandL()方法中,必须要对EEikCmdExit(以及任何特定平台相关的事件,例如Series 60上的EAknSoftkeyBack)做出响应。
     要对外来系统事件做出响应。请牢记,您的应用程序在一个多任务电话系统上运行,您需要将注意力集中于刚获得/丢失的事件上,以确保当用户获得一个高优先级的通知时您能够做出正确响应。例如,正打进来的电话会干扰您的应用程序的运行,这时应确保您已保存了系统状态和数据(即:您需要对标准的“背景”事件采取恰当的行动—请参阅SDK)。一般来说,系统框架会处理这个问题,您不需要采取任何特殊行动—但一定要确保您没有妨碍系统框架的正常操作。
     内存处理是Symbian OS需要考虑的一个重要课题。在这一点上,应注意电话有时会不同于模拟器。因此在将您的应用程序呈交给“Symbian 认证签名”进行测试之前,务必确保已经在实际电话设备上测试了您的程序。
     内存堆栈空间有限!应尽可能将对象放到内存堆中,而不要放到栈里。KERN-EXEC 3异常(panic)发生的主要原因之一就是栈的破坏/溢出。
     应用程序发生异常(panic)表明您的代码中一定有错误。以下是一些主要的、常见的错误:
Ⅰ:忘记将非对象成员、被分配到堆的变量加到CleanupStack上。
 Ⅱ:将成员变量放到CleanupStack上—这一点要千万避免(在析构函数中将这些变量删除就可以了)。
 Ⅲ:“重复删除”--例如,没有正确的从CleanupStack上Pop()出已经被销毁的对象,造成CleanupStack以后试图再次删除它。或者使用过一个对象之后将其删除但忘记将其值设成NULL,从而在析构函数里又试图删除一次。
  Ⅳ:用可能不存在于您的析构函数中的变量调用函数。例如,以下代码可能导致异常因为有可能您在分配内存之前您的对象已经被销毁,或者在应用程序的另一处已经删除了该内存,这样iSomeServer就会处于NULL:
     CMyClass::~CMyClass()
{
iSomeServer->Close();
delete iSomeServer;
}
应该如下编写该代码:
CMyClass::~CMyClass()
{
If(iSomeServer)
{
iSomeServer->Close();
delete iSomeServer;
}
}
  Ⅴ:在NULL指针上调用函数。
Ⅵ:函数调用另一个函数,而其使用的变量已经超出范畴,例如:
    把一个栈变量传送到一个异步函数的回调(callback)里。
     在系统资源不够的情况下,得体的处理失效情况是非常重要的。最受限制的资源通常是系统RAM,因此您需要注意正确的处理内存不足的情况。采用“两端构造方法”和如下所述的CleanupStack机制,对这种防御性编程来说是必不可少和极其重要的。
     对带“R”字头、具备Close()方法的类,总是使用CleanupClosePushL()。这将确保当Leave事件发生时,它们会被恰当的清除。例如:
RFile file;
User::LeaveIfError(file.Open(…));
CleanupClosePushL(file);
CleanupStack::PopAndDestroy(&file);
对用Release()或Destroy()的“R”类,亦可使用CleanupDeletePushL()及CleanupRelasePushL()来取代Close()。
     另外,请记住CleanupStack机制是可扩展的,面对所有Leave事件,都可以用它来有效的清除任何对象。即使您需要处理的是较复杂的情况,也不应该忽略采用正规的清理机制。欲进一步了解TCleanupItem,请参阅Symbian OS Library文档。
     倘若您意图对HBufC变量重新分配资源,在清除它们之后,总是将其设为NULL。由于HBufC的资源分配或其再分配可能会导致Leave事件的发生,从而可能会出现析构函数试图删除已经不存在的HBufC变量的情况。当然,对于任何由堆分配资源的变量而言都应如此,对HBufC变量采取此做法更是已经成为普遍的使用模式。
     当必须采用自己的TRAP时,请忽略所有的报错。常见的编码错误是:
TRAPD(err, DoSomethingL());
If(err == KErrNone || err == KErrNotFound)
{
//Do something else
}
这意味着其他错误码都被忽略。然而,倘若您非用上述模式不可,应采用Leave机制来处理其他错误:
TRAPD(err, DoSomethingL());
If(err == KErrNone||err == KerrNotFound)
{
//Do something else
}
else
User::Leave(err);
 
不要延迟将对象PushL()到CleanupStack上。所有新创建的对象(成员变量除外)应被立即压入该堆栈。例如,下面的做法是错的:
Void doExampleL()
{
CSomeObject* myObject1 = new (ELeave)CSomeObject;
CSomeObject* myObject2 = new (ELeave)CSomeObject;
//Do something here with the variables
CleanupStack::PushL(myObject1);
CleanupStack::PushL(myObject2);
//Do something more with the variables
CleanupStack::PopAndDestroy(2);
//myObject2,myObject1
}
因为myObject2的创建可能失败,造成myObject1”悬”在那里不能被清理。应该这样来实现:
Void doExampleL()
{
CSomeObject* myObject1 = new (ELeave)CSomeObject;
CleanupStack::PushL(myObject1);
CSomeObject* myObject2 = new (ELeave)CSomeObject;
CleanupStack::PushL(myObject2);
//Do something here with the variables
CleanupStack::PopAndDestroy(2);
//myObject2,myObject1
}
注意,那些名称有大写字母C结尾的函数(例如NewLC())会自动把其对象置于CleanupStack。您不应该自己来将这些对象压入CleanupStack,否则该对象会入栈两次。当您创建非成员变量并为其分配内存时,这些由C结尾的函数很有用。
“两端构造方法”是Symbian OS内存管理的关键部分。基本原则是Symbian OS中的构造函数或析构函数永远不应该发生Leave。倘若一个C++构造函数Leave,构造过程未完成的对象得不到清理,因为还没有生成指针指向该对象。为此,Symbian OS中的构造函数仅将该对象实例化,而后调用该对象的ConstructL()函数,在其中将成员数据实例化。一旦ConstructL()发生Leave,标准的析构函数将被调用来清除所有至此已被成功分配的成员变量。在您的编码中照用这一设计模式来防止内存泄漏,至为关键。当您写每一行代码时,都应该问自己:“这一行代码能否发生Leave?”假如回答为“是”,则应考虑“是否所有资源都将被释放?”。
编码中请勿使用_L()宏---而应使用_LIT()。_L()自Symbian OS V5起已是“不推荐使用”(deprecated),它的问题在于它将调用TPtrC(const TText*)构造函数,该构造函数会调用strlen()函数来计算该字串的长度。虽然这不会带来额外的RAM开销,却会在运行时占用更多CPU周期。相反,宏_LIT()直接创建了一个在编译时就全部实例化的对象,节省了构造TPtrC的CPU开销。当然,您首先应该考虑的是否应该使用硬编码的字符串常量,因为当您将来地方化(localize)您的程序时,这种常量类型的描述符(descriptor)可能需要重新编码(我觉得作者这里写的有问题,_LIT()及_L()定义的是常量字符串,并非描述符,它不能够用描述符的一些通用的方法,只有调用_LIT()和_L()重载的()运算符才会变成const TDesC&类型的描述符,并使TDesC的一些通用方法可用---孙东风注)
当在函数参数中使用描述符(descriptor)时,应缺省使用基类。在大多数情况下,以const TDesC&形式来传递描述符。对可修改的描述符,则应使用TDes&。
当在函数中传递或返回对象时,应确保如果您拥有该对象的所有权,您应负责将其清除!Symbian采取的约定是:函数中的指针表示所有权转移到调用者,而使用引用则表示被传递对象的所有权仍属于原所有者。
Active Objects是Symbian OS的重要特性之一。请仔细研究SDK文档、Symbian Developer NetWork白皮书,以充分理解其工作原理。下面有一些有用的窍门:
Ⅰ:在RunL()内无需使用TRAP()。Active Scheduler本身会TRAP函数RunL()并在其发Leave时调用CActive::RunError()。
Ⅱ:为此,您应实现自己的RunError()函数来处理从RunL()的Leave事件。
Ⅲ:保证RunL()操作尽可能简短。长时间运行的RunL()将阻塞其他Active Objects。
Ⅳ:总是实现DoCancel()函数,总是在AO析构函数中调用Cancel()。
您应尽可能利用Active Object框架机制。对于使用电池供电的设备,在一个循环中紧密不断地进行轮流检测(polling)是极其不适当的,将带来大量耗电。写游戏时,对此尤需特别注意,详情参阅Symbian Developer Network网站的技术文档:
www.symbian.com/developer/techlib/papers/porting_3D_games/XenGames_paper.pdf
ViewSrv 11异常对于繁忙运行的程序(例如游戏)是一个潜在的问题。当您的,或者其他任何程序中的ViewSrv active object不能及时响应View Server时就会导致此种异常。典型的最长回应时间是10-20秒。FAQ-0900有详细解释,FAQ-0920有针对如何避免此类问题的实用技巧。二者均可从www3.symbian.com/faq.nsf网页上的Symbian OS FAQ数据库获取。
您无需使用HBufC::Des()来进入一个HBufC对象。只需采用*操作符来为HBufC对象解除引用(dereference)。这对于向某个接受TDesC&(上文的推荐做法)的函数传递HBufC参数时尤其有用。
21当使用标准的程序.INI文件的功能时(即在您的应用UI类中使用Application()->OpenIniFileLC();API时),确保将版本号信息写入流(stream)中。这样使您能够在未来新版本的程序中建立新的流,意味着即使某个最终用户将来安装您的软件的新版本时,不会因为在旧的.INI文件中找不到正确配置或流时发生异常。
22.在您的程序中实现框架类(framework class)时要小心。应该始终从所提供的平台相关的框架类中继承。例如,对UIQ而言,不要从CQikAppUi继承。所有的应用基类(CQikAppUi、CQikApplication、CQikDocument)添加的功能支持更广的框架范围来保证应用程序正确运行。



  
     确保您的应用程序能够对系统关机事件做出响应。在您的AppUi::HandleCommandL()方法中,必须要对EEikCmdExit(以及任何特定平台相关的事件,例如Series 60上的EAknSoftkeyBack)做出响应。
     要对外来系统事件做出响应。请牢记,您的应用程序在一个多任务电话系统上运行,您需要将注意力集中于刚获得/丢失的事件上,以确保当用户获得一个高优先级的通知时您能够做出正确响应。例如,正打进来的电话会干扰您的应用程序的运行,这时应确保您已保存了系统状态和数据(即:您需要对标准的“背景”事件采取恰当的行动—请参阅SDK)。一般来说,系统框架会处理这个问题,您不需要采取任何特殊行动—但一定要确保您没有妨碍系统框架的正常操作。
     内存处理是Symbian OS需要考虑的一个重要课题。在这一点上,应注意电话有时会不同于模拟器。因此在将您的应用程序呈交给“Symbian 认证签名”进行测试之前,务必确保已经在实际电话设备上测试了您的程序。
     内存堆栈空间有限!应尽可能将对象放到内存堆中,而不要放到栈里。KERN-EXEC 3异常(panic)发生的主要原因之一就是栈的破坏/溢出。
     应用程序发生异常(panic)表明您的代码中一定有错误。以下是一些主要的、常见的错误:
Ⅰ:忘记将非对象成员、被分配到堆的变量加到CleanupStack上。
 Ⅱ:将成员变量放到CleanupStack上—这一点要千万避免(在析构函数中将这些变量删除就可以了)。
 Ⅲ:“重复删除”--例如,没有正确的从CleanupStack上Pop()出已经被销毁的对象,造成CleanupStack以后试图再次删除它。或者使用过一个对象之后将其删除但忘记将其值设成NULL,从而在析构函数里又试图删除一次。
  Ⅳ:用可能不存在于您的析构函数中的变量调用函数。例如,以下代码可能导致异常因为有可能您在分配内存之前您的对象已经被销毁,或者在应用程序的另一处已经删除了该内存,这样iSomeServer就会处于NULL:
     CMyClass::~CMyClass()
{
iSomeServer->Close();
delete iSomeServer;
}
应该如下编写该代码:
CMyClass::~CMyClass()
{
If(iSomeServer)
{
iSomeServer->Close();
delete iSomeServer;
}
}
  Ⅴ:在NULL指针上调用函数。
Ⅵ:函数调用另一个函数,而其使用的变量已经超出范畴,例如:
    把一个栈变量传送到一个异步函数的回调(callback)里。
     在系统资源不够的情况下,得体的处理失效情况是非常重要的。最受限制的资源通常是系统RAM,因此您需要注意正确的处理内存不足的情况。采用“两端构造方法”和如下所述的CleanupStack机制,对这种防御性编程来说是必不可少和极其重要的。
     对带“R”字头、具备Close()方法的类,总是使用CleanupClosePushL()。这将确保当Leave事件发生时,它们会被恰当的清除。例如:
RFile file;
User::LeaveIfError(file.Open(…));
CleanupClosePushL(file);
CleanupStack::PopAndDestroy(&file);
对用Release()或Destroy()的“R”类,亦可使用CleanupDeletePushL()及CleanupRelasePushL()来取代Close()。
     另外,请记住CleanupStack机制是可扩展的,面对所有Leave事件,都可以用它来有效的清除任何对象。即使您需要处理的是较复杂的情况,也不应该忽略采用正规的清理机制。欲进一步了解TCleanupItem,请参阅Symbian OS Library文档。
     倘若您意图对HBufC变量重新分配资源,在清除它们之后,总是将其设为NULL。由于HBufC的资源分配或其再分配可能会导致Leave事件的发生,从而可能会出现析构函数试图删除已经不存在的HBufC变量的情况。当然,对于任何由堆分配资源的变量而言都应如此,对HBufC变量采取此做法更是已经成为普遍的使用模式。
     当必须采用自己的TRAP时,请忽略所有的报错。常见的编码错误是:
TRAPD(err, DoSomethingL());
If(err == KErrNone || err == KErrNotFound)
{
//Do something else
}
这意味着其他错误码都被忽略。然而,倘若您非用上述模式不可,应采用Leave机制来处理其他错误:
TRAPD(err, DoSomethingL());
If(err == KErrNone||err == KerrNotFound)
{
//Do something else
}
else
User::Leave(err);
 
不要延迟将对象PushL()到CleanupStack上。所有新创建的对象(成员变量除外)应被立即压入该堆栈。例如,下面的做法是错的:
Void doExampleL()
{
CSomeObject* myObject1 = new (ELeave)CSomeObject;
CSomeObject* myObject2 = new (ELeave)CSomeObject;
//Do something here with the variables
CleanupStack::PushL(myObject1);
CleanupStack::PushL(myObject2);
//Do something more with the variables
CleanupStack::PopAndDestroy(2);
//myObject2,myObject1
}
因为myObject2的创建可能失败,造成myObject1”悬”在那里不能被清理。应该这样来实现:
Void doExampleL()
{
CSomeObject* myObject1 = new (ELeave)CSomeObject;
CleanupStack::PushL(myObject1);
CSomeObject* myObject2 = new (ELeave)CSomeObject;
CleanupStack::PushL(myObject2);
//Do something here with the variables
CleanupStack::PopAndDestroy(2);
//myObject2,myObject1
}
注意,那些名称有大写字母C结尾的函数(例如NewLC())会自动把其对象置于CleanupStack。您不应该自己来将这些对象压入CleanupStack,否则该对象会入栈两次。当您创建非成员变量并为其分配内存时,这些由C结尾的函数很有用。
“两端构造方法”是Symbian OS内存管理的关键部分。基本原则是Symbian OS中的构造函数或析构函数永远不应该发生Leave。倘若一个C++构造函数Leave,构造过程未完成的对象得不到清理,因为还没有生成指针指向该对象。为此,Symbian OS中的构造函数仅将该对象实例化,而后调用该对象的ConstructL()函数,在其中将成员数据实例化。一旦ConstructL()发生Leave,标准的析构函数将被调用来清除所有至此已被成功分配的成员变量。在您的编码中照用这一设计模式来防止内存泄漏,至为关键。当您写每一行代码时,都应该问自己:“这一行代码能否发生Leave?”假如回答为“是”,则应考虑“是否所有资源都将被释放?”。
编码中请勿使用_L()宏---而应使用_LIT()。_L()自Symbian OS V5起已是“不推荐使用”(deprecated),它的问题在于它将调用TPtrC(const TText*)构造函数,该构造函数会调用strlen()函数来计算该字串的长度。虽然这不会带来额外的RAM开销,却会在运行时占用更多CPU周期。相反,宏_LIT()直接创建了一个在编译时就全部实例化的对象,节省了构造TPtrC的CPU开销。当然,您首先应该考虑的是否应该使用硬编码的字符串常量,因为当您将来地方化(localize)您的程序时,这种常量类型的描述符(descriptor)可能需要重新编码(我觉得作者这里写的有问题,_LIT()及_L()定义的是常量字符串,并非描述符,它不能够用描述符的一些通用的方法,只有调用_LIT()和_L()重载的()运算符才会变成const TDesC&类型的描述符,并使TDesC的一些通用方法可用---孙东风注)
当在函数参数中使用描述符(descriptor)时,应缺省使用基类。在大多数情况下,以const TDesC&形式来传递描述符。对可修改的描述符,则应使用TDes&。
当在函数中传递或返回对象时,应确保如果您拥有该对象的所有权,您应负责将其清除!Symbian采取的约定是:函数中的指针表示所有权转移到调用者,而使用引用则表示被传递对象的所有权仍属于原所有者。
Active Objects是Symbian OS的重要特性之一。请仔细研究SDK文档、Symbian Developer NetWork白皮书,以充分理解其工作原理。下面有一些有用的窍门:
Ⅰ:在RunL()内无需使用TRAP()。Active Scheduler本身会TRAP函数RunL()并在其发Leave时调用CActive::RunError()。
Ⅱ:为此,您应实现自己的RunError()函数来处理从RunL()的Leave事件。
Ⅲ:保证RunL()操作尽可能简短。长时间运行的RunL()将阻塞其他Active Objects。
Ⅳ:总是实现DoCancel()函数,总是在AO析构函数中调用Cancel()。
您应尽可能利用Active Object框架机制。对于使用电池供电的设备,在一个循环中紧密不断地进行轮流检测(polling)是极其不适当的,将带来大量耗电。写游戏时,对此尤需特别注意,详情参阅Symbian Developer Network网站的技术文档:
www.symbian.com/developer/techlib/papers/porting_3D_games/XenGames_paper.pdf
ViewSrv 11异常对于繁忙运行的程序(例如游戏)是一个潜在的问题。当您的,或者其他任何程序中的ViewSrv active object不能及时响应View Server时就会导致此种异常。典型的最长回应时间是10-20秒。FAQ-0900有详细解释,FAQ-0920有针对如何避免此类问题的实用技巧。二者均可从www3.symbian.com/faq.nsf网页上的Symbian OS FAQ数据库获取。
您无需使用HBufC::Des()来进入一个HBufC对象。只需采用*操作符来为HBufC对象解除引用(dereference)。这对于向某个接受TDesC&(上文的推荐做法)的函数传递HBufC参数时尤其有用。
21当使用标准的程序.INI文件的功能时(即在您的应用UI类中使用Application()->OpenIniFileLC();API时),确保将版本号信息写入流(stream)中。这样使您能够在未来新版本的程序中建立新的流,意味着即使某个最终用户将来安装您的软件的新版本时,不会因为在旧的.INI文件中找不到正确配置或流时发生异常。
22.在您的程序中实现框架类(framework class)时要小心。应该始终从所提供的平台相关的框架类中继承。例如,对UIQ而言,不要从CQikAppUi继承。所有的应用基类(CQikAppUi、CQikApplication、CQikDocument)添加的功能支持更广的框架范围来保证应用程序正确运行。



  
     确保您的应用程序能够对系统关机事件做出响应。在您的AppUi::HandleCommandL()方法中,必须要对EEikCmdExit(以及任何特定平台相关的事件,例如Series 60上的EAknSoftkeyBack)做出响应。
     要对外来系统事件做出响应。请牢记,您的应用程序在一个多任务电话系统上运行,您需要将注意力集中于刚获得/丢失的事件上,以确保当用户获得一个高优先级的通知时您能够做出正确响应。例如,正打进来的电话会干扰您的应用程序的运行,这时应确保您已保存了系统状态和数据(即:您需要对标准的“背景”事件采取恰当的行动—请参阅SDK)。一般来说,系统框架会处理这个问题,您不需要采取任何特殊行动—但一定要确保您没有妨碍系统框架的正常操作。
     内存处理是Symbian OS需要考虑的一个重要课题。在这一点上,应注意电话有时会不同于模拟器。因此在将您的应用程序呈交给“Symbian 认证签名”进行测试之前,务必确保已经在实际电话设备上测试了您的程序。
     内存堆栈空间有限!应尽可能将对象放到内存堆中,而不要放到栈里。KERN-EXEC 3异常(panic)发生的主要原因之一就是栈的破坏/溢出。
     应用程序发生异常(panic)表明您的代码中一定有错误。以下是一些主要的、常见的错误:
Ⅰ:忘记将非对象成员、被分配到堆的变量加到CleanupStack上。
 Ⅱ:将成员变量放到CleanupStack上—这一点要千万避免(在析构函数中将这些变量删除就可以了)。
 Ⅲ:“重复删除”--例如,没有正确的从CleanupStack上Pop()出已经被销毁的对象,造成CleanupStack以后试图再次删除它。或者使用过一个对象之后将其删除但忘记将其值设成NULL,从而在析构函数里又试图删除一次。
  Ⅳ:用可能不存在于您的析构函数中的变量调用函数。例如,以下代码可能导致异常因为有可能您在分配内存之前您的对象已经被销毁,或者在应用程序的另一处已经删除了该内存,这样iSomeServer就会处于NULL:
     CMyClass::~CMyClass()
{
iSomeServer->Close();
delete iSomeServer;
}
应该如下编写该代码:
CMyClass::~CMyClass()
{
If(iSomeServer)
{
iSomeServer->Close();
delete iSomeServer;
}
}
  Ⅴ:在NULL指针上调用函数。
Ⅵ:函数调用另一个函数,而其使用的变量已经超出范畴,例如:
    把一个栈变量传送到一个异步函数的回调(callback)里。
     在系统资源不够的情况下,得体的处理失效情况是非常重要的。最受限制的资源通常是系统RAM,因此您需要注意正确的处理内存不足的情况。采用“两端构造方法”和如下所述的CleanupStack机制,对这种防御性编程来说是必不可少和极其重要的。
     对带“R”字头、具备Close()方法的类,总是使用CleanupClosePushL()。这将确保当Leave事件发生时,它们会被恰当的清除。例如:
RFile file;
User::LeaveIfError(file.Open(…));
CleanupClosePushL(file);
CleanupStack::PopAndDestroy(&file);
对用Release()或Destroy()的“R”类,亦可使用CleanupDeletePushL()及CleanupRelasePushL()来取代Close()。
     另外,请记住CleanupStack机制是可扩展的,面对所有Leave事件,都可以用它来有效的清除任何对象。即使您需要处理的是较复杂的情况,也不应该忽略采用正规的清理机制。欲进一步了解TCleanupItem,请参阅Symbian OS Library文档。
     倘若您意图对HBufC变量重新分配资源,在清除它们之后,总是将其设为NULL。由于HBufC的资源分配或其再分配可能会导致Leave事件的发生,从而可能会出现析构函数试图删除已经不存在的HBufC变量的情况。当然,对于任何由堆分配资源的变量而言都应如此,对HBufC变量采取此做法更是已经成为普遍的使用模式。
     当必须采用自己的TRAP时,请忽略所有的报错。常见的编码错误是:
TRAPD(err, DoSomethingL());
If(err == KErrNone || err == KErrNotFound)
{
//Do something else
}
这意味着其他错误码都被忽略。然而,倘若您非用上述模式不可,应采用Leave机制来处理其他错误:
TRAPD(err, DoSomethingL());
If(err == KErrNone||err == KerrNotFound)
{
//Do something else
}
else
User::Leave(err);
 
不要延迟将对象PushL()到CleanupStack上。所有新创建的对象(成员变量除外)应被立即压入该堆栈。例如,下面的做法是错的:
Void doExampleL()
{
CSomeObject* myObject1 = new (ELeave)CSomeObject;
CSomeObject* myObject2 = new (ELeave)CSomeObject;
//Do something here with the variables
CleanupStack::PushL(myObject1);
CleanupStack::PushL(myObject2);
//Do something more with the variables
CleanupStack::PopAndDestroy(2);
//myObject2,myObject1
}
因为myObject2的创建可能失败,造成myObject1”悬”在那里不能被清理。应该这样来实现:
Void doExampleL()
{
CSomeObject* myObject1 = new (ELeave)CSomeObject;
CleanupStack::PushL(myObject1);
CSomeObject* myObject2 = new (ELeave)CSomeObject;
CleanupStack::PushL(myObject2);
//Do something here with the variables
CleanupStack::PopAndDestroy(2);
//myObject2,myObject1
}
注意,那些名称有大写字母C结尾的函数(例如NewLC())会自动把其对象置于CleanupStack。您不应该自己来将这些对象压入CleanupStack,否则该对象会入栈两次。当您创建非成员变量并为其分配内存时,这些由C结尾的函数很有用。
“两端构造方法”是Symbian OS内存管理的关键部分。基本原则是Symbian OS中的构造函数或析构函数永远不应该发生Leave。倘若一个C++构造函数Leave,构造过程未完成的对象得不到清理,因为还没有生成指针指向该对象。为此,Symbian OS中的构造函数仅将该对象实例化,而后调用该对象的ConstructL()函数,在其中将成员数据实例化。一旦ConstructL()发生Leave,标准的析构函数将被调用来清除所有至此已被成功分配的成员变量。在您的编码中照用这一设计模式来防止内存泄漏,至为关键。当您写每一行代码时,都应该问自己:“这一行代码能否发生Leave?”假如回答为“是”,则应考虑“是否所有资源都将被释放?”。
编码中请勿使用_L()宏---而应使用_LIT()。_L()自Symbian OS V5起已是“不推荐使用”(deprecated),它的问题在于它将调用TPtrC(const TText*)构造函数,该构造函数会调用strlen()函数来计算该字串的长度。虽然这不会带来额外的RAM开销,却会在运行时占用更多CPU周期。相反,宏_LIT()直接创建了一个在编译时就全部实例化的对象,节省了构造TPtrC的CPU开销。当然,您首先应该考虑的是否应该使用硬编码的字符串常量,因为当您将来地方化(localize)您的程序时,这种常量类型的描述符(descriptor)可能需要重新编码(我觉得作者这里写的有问题,_LIT()及_L()定义的是常量字符串,并非描述符,它不能够用描述符的一些通用的方法,只有调用_LIT()和_L()重载的()运算符才会变成const TDesC&类型的描述符,并使TDesC的一些通用方法可用---孙东风注)
当在函数参数中使用描述符(descriptor)时,应缺省使用基类。在大多数情况下,以const TDesC&形式来传递描述符。对可修改的描述符,则应使用TDes&。
当在函数中传递或返回对象时,应确保如果您拥有该对象的所有权,您应负责将其清除!Symbian采取的约定是:函数中的指针表示所有权转移到调用者,而使用引用则表示被传递对象的所有权仍属于原所有者。
Active Objects是Symbian OS的重要特性之一。请仔细研究SDK文档、Symbian Developer NetWork白皮书,以充分理解其工作原理。下面有一些有用的窍门:
Ⅰ:在RunL()内无需使用TRAP()。Active Scheduler本身会TRAP函数RunL()并在其发Leave时调用CActive::RunError()。
Ⅱ:为此,您应实现自己的RunError()函数来处理从RunL()的Leave事件。
Ⅲ:保证RunL()操作尽可能简短。长时间运行的RunL()将阻塞其他Active Objects。
Ⅳ:总是实现DoCancel()函数,总是在AO析构函数中调用Cancel()。
您应尽可能利用Active Object框架机制。对于使用电池供电的设备,在一个循环中紧密不断地进行轮流检测(polling)是极其不适当的,将带来大量耗电。写游戏时,对此尤需特别注意,详情参阅Symbian Developer Network网站的技术文档:
www.symbian.com/developer/techlib/papers/porting_3D_games/XenGames_paper.pdf
ViewSrv 11异常对于繁忙运行的程序(例如游戏)是一个潜在的问题。当您的,或者其他任何程序中的ViewSrv active object不能及时响应View Server时就会导致此种异常。典型的最长回应时间是10-20秒。FAQ-0900有详细解释,FAQ-0920有针对如何避免此类问题的实用技巧。二者均可从www3.symbian.com/faq.nsf网页上的Symbian OS FAQ数据库获取。
您无需使用HBufC::Des()来进入一个HBufC对象。只需采用*操作符来为HBufC对象解除引用(dereference)。这对于向某个接受TDesC&(上文的推荐做法)的函数传递HBufC参数时尤其有用。
21当使用标准的程序.INI文件的功能时(即在您的应用UI类中使用Application()->OpenIniFileLC();API时),确保将版本号信息写入流(stream)中。这样使您能够在未来新版本的程序中建立新的流,意味着即使某个最终用户将来安装您的软件的新版本时,不会因为在旧的.INI文件中找不到正确配置或流时发生异常。
22.在您的程序中实现框架类(framework class)时要小心。应该始终从所提供的平台相关的框架类中继承。例如,对UIQ而言,不要从CQikAppUi继承。所有的应用基类(CQikAppUi、CQikApplication、CQikDocument)添加的功能支持更广的框架范围来保证应用程序正确运行。



  
     确保您的应用程序能够对系统关机事件做出响应。在您的AppUi::HandleCommandL()方法中,必须要对EEikCmdExit(以及任何特定平台相关的事件,例如Series 60上的EAknSoftkeyBack)做出响应。
     要对外来系统事件做出响应。请牢记,您的应用程序在一个多任务电话系统上运行,您需要将注意力集中于刚获得/丢失的事件上,以确保当用户获得一个高优先级的通知时您能够做出正确响应。例如,正打进来的电话会干扰您的应用程序的运行,这时应确保您已保存了系统状态和数据(即:您需要对标准的“背景”事件采取恰当的行动—请参阅SDK)。一般来说,系统框架会处理这个问题,您不需要采取任何特殊行动—但一定要确保您没有妨碍系统框架的正常操作。
     内存处理是Symbian OS需要考虑的一个重要课题。在这一点上,应注意电话有时会不同于模拟器。因此在将您的应用程序呈交给“Symbian 认证签名”进行测试之前,务必确保已经在实际电话设备上测试了您的程序。
     内存堆栈空间有限!应尽可能将对象放到内存堆中,而不要放到栈里。KERN-EXEC 3异常(panic)发生的主要原因之一就是栈的破坏/溢出。
     应用程序发生异常(panic)表明您的代码中一定有错误。以下是一些主要的、常见的错误:
Ⅰ:忘记将非对象成员、被分配到堆的变量加到CleanupStack上。
 Ⅱ:将成员变量放到CleanupStack上—这一点要千万避免(在析构函数中将这些变量删除就可以了)。
 Ⅲ:“重复删除”--例如,没有正确的从CleanupStack上Pop()出已经被销毁的对象,造成CleanupStack以后试图再次删除它。或者使用过一个对象之后将其删除但忘记将其值设成NULL,从而在析构函数里又试图删除一次。
  Ⅳ:用可能不存在于您的析构函数中的变量调用函数。例如,以下代码可能导致异常因为有可能您在分配内存之前您的对象已经被销毁,或者在应用程序的另一处已经删除了该内存,这样iSomeServer就会处于NULL:
     CMyClass::~CMyClass()
{
iSomeServer->Close();
delete iSomeServer;
}
应该如下编写该代码:
CMyClass::~CMyClass()
{
If(iSomeServer)
{
iSomeServer->Close();
delete iSomeServer;
}
}
  Ⅴ:在NULL指针上调用函数。
Ⅵ:函数调用另一个函数,而其使用的变量已经超出范畴,例如:
    把一个栈变量传送到一个异步函数的回调(callback)里。
     在系统资源不够的情况下,得体的处理失效情况是非常重要的。最受限制的资源通常是系统RAM,因此您需要注意正确的处理内存不足的情况。采用“两端构造方法”和如下所述的CleanupStack机制,对这种防御性编程来说是必不可少和极其重要的。
     对带“R”字头、具备Close()方法的类,总是使用CleanupClosePushL()。这将确保当Leave事件发生时,它们会被恰当的清除。例如:
RFile file;
User::LeaveIfError(file.Open(…));
CleanupClosePushL(file);
CleanupStack::PopAndDestroy(&file);
对用Release()或Destroy()的“R”类,亦可使用CleanupDeletePushL()及CleanupRelasePushL()来取代Close()。
     另外,请记住CleanupStack机制是可扩展的,面对所有Leave事件,都可以用它来有效的清除任何对象。即使您需要处理的是较复杂的情况,也不应该忽略采用正规的清理机制。欲进一步了解TCleanupItem,请参阅Symbian OS Library文档。
     倘若您意图对HBufC变量重新分配资源,在清除它们之后,总是将其设为NULL。由于HBufC的资源分配或其再分配可能会导致Leave事件的发生,从而可能会出现析构函数试图删除已经不存在的HBufC变量的情况。当然,对于任何由堆分配资源的变量而言都应如此,对HBufC变量采取此做法更是已经成为普遍的使用模式。
     当必须采用自己的TRAP时,请忽略所有的报错。常见的编码错误是:
TRAPD(err, DoSomethingL());
If(err == KErrNone || err == KErrNotFound)
{
//Do something else
}
这意味着其他错误码都被忽略。然而,倘若您非用上述模式不可,应采用Leave机制来处理其他错误:
TRAPD(err, DoSomethingL());
If(err == KErrNone||err == KerrNotFound)
{
//Do something else
}
else
User::Leave(err);
 
不要延迟将对象PushL()到CleanupStack上。所有新创建的对象(成员变量除外)应被立即压入该堆栈。例如,下面的做法是错的:
Void doExampleL()
{
CSomeObject* myObject1 = new (ELeave)CSomeObject;
CSomeObject* myObject2 = new (ELeave)CSomeObject;
//Do something here with the variables
CleanupStack::PushL(myObject1);
CleanupStack::PushL(myObject2);
//Do something more with the variables
CleanupStack::PopAndDestroy(2);
//myObject2,myObject1
}
因为myObject2的创建可能失败,造成myObject1”悬”在那里不能被清理。应该这样来实现:
Void doExampleL()
{
CSomeObject* myObject1 = new (ELeave)CSomeObject;
CleanupStack::PushL(myObject1);
CSomeObject* myObject2 = new (ELeave)CSomeObject;
CleanupStack::PushL(myObject2);
//Do something here with the variables
CleanupStack::PopAndDestroy(2);
//myObject2,myObject1
}
注意,那些名称有大写字母C结尾的函数(例如NewLC())会自动把其对象置于CleanupStack。您不应该自己来将这些对象压入CleanupStack,否则该对象会入栈两次。当您创建非成员变量并为其分配内存时,这些由C结尾的函数很有用。
“两端构造方法”是Symbian OS内存管理的关键部分。基本原则是Symbian OS中的构造函数或析构函数永远不应该发生Leave。倘若一个C++构造函数Leave,构造过程未完成的对象得不到清理,因为还没有生成指针指向该对象。为此,Symbian OS中的构造函数仅将该对象实例化,而后调用该对象的ConstructL()函数,在其中将成员数据实例化。一旦ConstructL()发生Leave,标准的析构函数将被调用来清除所有至此已被成功分配的成员变量。在您的编码中照用这一设计模式来防止内存泄漏,至为关键。当您写每一行代码时,都应该问自己:“这一行代码能否发生Leave?”假如回答为“是”,则应考虑“是否所有资源都将被释放?”。
编码中请勿使用_L()宏---而应使用_LIT()。_L()自Symbian OS V5起已是“不推荐使用”(deprecated),它的问题在于它将调用TPtrC(const TText*)构造函数,该构造函数会调用strlen()函数来计算该字串的长度。虽然这不会带来额外的RAM开销,却会在运行时占用更多CPU周期。相反,宏_LIT()直接创建了一个在编译时就全部实例化的对象,节省了构造TPtrC的CPU开销。当然,您首先应该考虑的是否应该使用硬编码的字符串常量,因为当您将来地方化(localize)您的程序时,这种常量类型的描述符(descriptor)可能需要重新编码(我觉得作者这里写的有问题,_LIT()及_L()定义的是常量字符串,并非描述符,它不能够用描述符的一些通用的方法,只有调用_LIT()和_L()重载的()运算符才会变成const TDesC&类型的描述符,并使TDesC的一些通用方法可用---孙东风注)
当在函数参数中使用描述符(descriptor)时,应缺省使用基类。在大多数情况下,以const TDesC&形式来传递描述符。对可修改的描述符,则应使用TDes&。
当在函数中传递或返回对象时,应确保如果您拥有该对象的所有权,您应负责将其清除!Symbian采取的约定是:函数中的指针表示所有权转移到调用者,而使用引用则表示被传递对象的所有权仍属于原所有者。
Active Objects是Symbian OS的重要特性之一。请仔细研究SDK文档、Symbian Developer NetWork白皮书,以充分理解其工作原理。下面有一些有用的窍门:
Ⅰ:在RunL()内无需使用TRAP()。Active Scheduler本身会TRAP函数RunL()并在其发Leave时调用CActive::RunError()。
Ⅱ:为此,您应实现自己的RunError()函数来处理从RunL()的Leave事件。
Ⅲ:保证RunL()操作尽可能简短。长时间运行的RunL()将阻塞其他Active Objects。
Ⅳ:总是实现DoCancel()函数,总是在AO析构函数中调用Cancel()。
您应尽可能利用Active Object框架机制。对于使用电池供电的设备,在一个循环中紧密不断地进行轮流检测(polling)是极其不适当的,将带来大量耗电。写游戏时,对此尤需特别注意,详情参阅Symbian Developer Network网站的技术文档:
www.symbian.com/developer/techlib/papers/porting_3D_games/XenGames_paper.pdf
ViewSrv 11异常对于繁忙运行的程序(例如游戏)是一个潜在的问题。当您的,或者其他任何程序中的ViewSrv active object不能及时响应View Server时就会导致此种异常。典型的最长回应时间是10-20秒。FAQ-0900有详细解释,FAQ-0920有针对如何避免此类问题的实用技巧。二者均可从www3.symbian.com/faq.nsf网页上的Symbian OS FAQ数据库获取。
您无需使用HBufC::Des()来进入一个HBufC对象。只需采用*操作符来为HBufC对象解除引用(dereference)。这对于向某个接受TDesC&(上文的推荐做法)的函数传递HBufC参数时尤其有用。
21当使用标准的程序.INI文件的功能时(即在您的应用UI类中使用Application()->OpenIniFileLC();API时),确保将版本号信息写入流(stream)中。这样使您能够在未来新版本的程序中建立新的流,意味着即使某个最终用户将来安装您的软件的新版本时,不会因为在旧的.INI文件中找不到正确配置或流时发生异常。
22.在您的程序中实现框架类(framework class)时要小心。应该始终从所提供的平台相关的框架类中继承。例如,对UIQ而言,不要从CQikAppUi继承。所有的应用基类(CQikAppUi、CQikApplication、CQikDocument)添加的功能支持更广的框架范围来保证应用程序正确运行。



  
     确保您的应用程序能够对系统关机事件做出响应。在您的AppUi::HandleCommandL()方法中,必须要对EEikCmdExit(以及任何特定平台相关的事件,例如Series 60上的EAknSoftkeyBack)做出响应。
     要对外来系统事件做出响应。请牢记,您的应用程序在一个多任务电话系统上运行,您需要将注意力集中于刚获得/丢失的事件上,以确保当用户获得一个高优先级的通知时您能够做出正确响应。例如,正打进来的电话会干扰您的应用程序的运行,这时应确保您已保存了系统状态和数据(即:您需要对标准的“背景”事件采取恰当的行动—请参阅SDK)。一般来说,系统框架会处理这个问题,您不需要采取任何特殊行动—但一定要确保您没有妨碍系统框架的正常操作。
     内存处理是Symbian OS需要考虑的一个重要课题。在这一点上,应注意电话有时会不同于模拟器。因此在将您的应用程序呈交给“Symbian 认证签名”进行测试之前,务必确保已经在实际电话设备上测试了您的程序。
     内存堆栈空间有限!应尽可能将对象放到内存堆中,而不要放到栈里。KERN-EXEC 3异常(panic)发生的主要原因之一就是栈的破坏/溢出。
     应用程序发生异常(panic)表明您的代码中一定有错误。以下是一些主要的、常见的错误:
Ⅰ:忘记将非对象成员、被分配到堆的变量加到CleanupStack上。
 Ⅱ:将成员变量放到CleanupStack上—这一点要千万避免(在析构函数中将这些变量删除就可以了)。
 Ⅲ:“重复删除”--例如,没有正确的从CleanupStack上Pop()出已经被销毁的对象,造成CleanupStack以后试图再次删除它。或者使用过一个对象之后将其删除但忘记将其值设成NULL,从而在析构函数里又试图删除一次。
  Ⅳ:用可能不存在于您的析构函数中的变量调用函数。例如,以下代码可能导致异常因为有可能您在分配内存之前您的对象已经被销毁,或者在应用程序的另一处已经删除了该内存,这样iSomeServer就会处于NULL:
     CMyClass::~CMyClass()
{
iSomeServer->Close();
delete iSomeServer;
}
应该如下编写该代码:
CMyClass::~CMyClass()
{
If(iSomeServer)
{
iSomeServer->Close();
delete iSomeServer;
}
}
  Ⅴ:在NULL指针上调用函数。
Ⅵ:函数调用另一个函数,而其使用的变量已经超出范畴,例如:
    把一个栈变量传送到一个异步函数的回调(callback)里。
     在系统资源不够的情况下,得体的处理失效情况是非常重要的。最受限制的资源通常是系统RAM,因此您需要注意正确的处理内存不足的情况。采用“两端构造方法”和如下所述的CleanupStack机制,对这种防御性编程来说是必不可少和极其重要的。
     对带“R”字头、具备Close()方法的类,总是使用CleanupClosePushL()。这将确保当Leave事件发生时,它们会被恰当的清除。例如:
RFile file;
User::LeaveIfError(file.Open(…));
CleanupClosePushL(file);
CleanupStack::PopAndDestroy(&file);
对用Release()或Destroy()的“R”类,亦可使用CleanupDeletePushL()及CleanupRelasePushL()来取代Close()。
     另外,请记住CleanupStack机制是可扩展的,面对所有Leave事件,都可以用它来有效的清除任何对象。即使您需要处理的是较复杂的情况,也不应该忽略采用正规的清理机制。欲进一步了解TCleanupItem,请参阅Symbian OS Library文档。
     倘若您意图对HBufC变量重新分配资源,在清除它们之后,总是将其设为NULL。由于HBufC的资源分配或其再分配可能会导致Leave事件的发生,从而可能会出现析构函数试图删除已经不存在的HBufC变量的情况。当然,对于任何由堆分配资源的变量而言都应如此,对HBufC变量采取此做法更是已经成为普遍的使用模式。
     当必须采用自己的TRAP时,请忽略所有的报错。常见的编码错误是:
TRAPD(err, DoSomethingL());
If(err == KErrNone || err == KErrNotFound)
{
//Do something else
}
这意味着其他错误码都被忽略。然而,倘若您非用上述模式不可,应采用Leave机制来处理其他错误:
TRAPD(err, DoSomethingL());
If(err == KErrNone||err == KerrNotFound)
{
//Do something else
}
else
User::Leave(err);
 
不要延迟将对象PushL()到CleanupStack上。所有新创建的对象(成员变量除外)应被立即压入该堆栈。例如,下面的做法是错的:
Void doExampleL()
{
CSomeObject* myObject1 = new (ELeave)CSomeObject;
CSomeObject* myObject2 = new (ELeave)CSomeObject;
//Do something here with the variables
CleanupStack::PushL(myObject1);
CleanupStack::PushL(myObject2);
//Do something more with the variables
CleanupStack::PopAndDestroy(2);
//myObject2,myObject1
}
因为myObject2的创建可能失败,造成myObject1”悬”在那里不能被清理。应该这样来实现:
Void doExampleL()
{
CSomeObject* myObject1 = new (ELeave)CSomeObject;
CleanupStack::PushL(myObject1);
CSomeObject* myObject2 = new (ELeave)CSomeObject;
CleanupStack::PushL(myObject2);
//Do something here with the variables
CleanupStack::PopAndDestroy(2);
//myObject2,myObject1
}
注意,那些名称有大写字母C结尾的函数(例如NewLC())会自动把其对象置于CleanupStack。您不应该自己来将这些对象压入CleanupStack,否则该对象会入栈两次。当您创建非成员变量并为其分配内存时,这些由C结尾的函数很有用。
“两端构造方法”是Symbian OS内存管理的关键部分。基本原则是Symbian OS中的构造函数或析构函数永远不应该发生Leave。倘若一个C++构造函数Leave,构造过程未完成的对象得不到清理,因为还没有生成指针指向该对象。为此,Symbian OS中的构造函数仅将该对象实例化,而后调用该对象的ConstructL()函数,在其中将成员数据实例化。一旦ConstructL()发生Leave,标准的析构函数将被调用来清除所有至此已被成功分配的成员变量。在您的编码中照用这一设计模式来防止内存泄漏,至为关键。当您写每一行代码时,都应该问自己:“这一行代码能否发生Leave?”假如回答为“是”,则应考虑“是否所有资源都将被释放?”。
编码中请勿使用_L()宏---而应使用_LIT()。_L()自Symbian OS V5起已是“不推荐使用”(deprecated),它的问题在于它将调用TPtrC(const TText*)构造函数,该构造函数会调用strlen()函数来计算该字串的长度。虽然这不会带来额外的RAM开销,却会在运行时占用更多CPU周期。相反,宏_LIT()直接创建了一个在编译时就全部实例化的对象,节省了构造TPtrC的CPU开销。当然,您首先应该考虑的是否应该使用硬编码的字符串常量,因为当您将来地方化(localize)您的程序时,这种常量类型的描述符(descriptor)可能需要重新编码(我觉得作者这里写的有问题,_LIT()及_L()定义的是常量字符串,并非描述符,它不能够用描述符的一些通用的方法,只有调用_LIT()和_L()重载的()运算符才会变成const TDesC&类型的描述符,并使TDesC的一些通用方法可用---孙东风注)
当在函数参数中使用描述符(descriptor)时,应缺省使用基类。在大多数情况下,以const TDesC&形式来传递描述符。对可修改的描述符,则应使用TDes&。
当在函数中传递或返回对象时,应确保如果您拥有该对象的所有权,您应负责将其清除!Symbian采取的约定是:函数中的指针表示所有权转移到调用者,而使用引用则表示被传递对象的所有权仍属于原所有者。
Active Objects是Symbian OS的重要特性之一。请仔细研究SDK文档、Symbian Developer NetWork白皮书,以充分理解其工作原理。下面有一些有用的窍门:
Ⅰ:在RunL()内无需使用TRAP()。Active Scheduler本身会TRAP函数RunL()并在其发Leave时调用CActive::RunError()。
Ⅱ:为此,您应实现自己的RunError()函数来处理从RunL()的Leave事件。
Ⅲ:保证RunL()操作尽可能简短。长时间运行的RunL()将阻塞其他Active Objects。
Ⅳ:总是实现DoCancel()函数,总是在AO析构函数中调用Cancel()。
您应尽可能利用Active Object框架机制。对于使用电池供电的设备,在一个循环中紧密不断地进行轮流检测(polling)是极其不适当的,将带来大量耗电。写游戏时,对此尤需特别注意,详情参阅Symbian Developer Network网站的技术文档:
www.symbian.com/developer/techlib/papers/porting_3D_games/XenGames_paper.pdf
ViewSrv 11异常对于繁忙运行的程序(例如游戏)是一个潜在的问题。当您的,或者其他任何程序中的ViewSrv active object不能及时响应View Server时就会导致此种异常。典型的最长回应时间是10-20秒。FAQ-0900有详细解释,FAQ-0920有针对如何避免此类问题的实用技巧。二者均可从www3.symbian.com/faq.nsf网页上的Symbian OS FAQ数据库获取。
您无需使用HBufC::Des()来进入一个HBufC对象。只需采用*操作符来为HBufC对象解除引用(dereference)。这对于向某个接受TDesC&(上文的推荐做法)的函数传递HBufC参数时尤其有用。
21当使用标准的程序.INI文件的功能时(即在您的应用UI类中使用Application()->OpenIniFileLC();API时),确保将版本号信息写入流(stream)中。这样使您能够在未来新版本的程序中建立新的流,意味着即使某个最终用户将来安装您的软件的新版本时,不会因为在旧的.INI文件中找不到正确配置或流时发生异常。
22.在您的程序中实现框架类(framework class)时要小心。应该始终从所提供的平台相关的框架类中继承。例如,对UIQ而言,不要从CQikAppUi继承。所有的应用基类(CQikAppUi、CQikApplication、CQikDocument)添加的功能支持更广的框架范围来保证应用程序正确运行。



  
     确保您的应用程序能够对系统关机事件做出响应。在您的AppUi::HandleCommandL()方法中,必须要对EEikCmdExit(以及任何特定平台相关的事件,例如Series 60上的EAknSoftkeyBack)做出响应。
     要对外来系统事件做出响应。请牢记,您的应用程序在一个多任务电话系统上运行,您需要将注意力集中于刚获得/丢失的事件上,以确保当用户获得一个高优先级的通知时您能够做出正确响应。例如,正打进来的电话会干扰您的应用程序的运行,这时应确保您已保存了系统状态和数据(即:您需要对标准的“背景”事件采取恰当的行动—请参阅SDK)。一般来说,系统框架会处理这个问题,您不需要采取任何特殊行动—但一定要确保您没有妨碍系统框架的正常操作。
     内存处理是Symbian OS需要考虑的一个重要课题。在这一点上,应注意电话有时会不同于模拟器。因此在将您的应用程序呈交给“Symbian 认证签名”进行测试之前,务必确保已经在实际电话设备上测试了您的程序。
     内存堆栈空间有限!应尽可能将对象放到内存堆中,而不要放到栈里。KERN-EXEC 3异常(panic)发生的主要原因之一就是栈的破坏/溢出。
     应用程序发生异常(panic)表明您的代码中一定有错误。以下是一些主要的、常见的错误:
Ⅰ:忘记将非对象成员、被分配到堆的变量加到CleanupStack上。
 Ⅱ:将成员变量放到CleanupStack上—这一点要千万避免(在析构函数中将这些变量删除就可以了)。
 Ⅲ:“重复删除”--例如,没有正确的从CleanupStack上Pop()出已经被销毁的对象,造成CleanupStack以后试图再次删除它。或者使用过一个对象之后将其删除但忘记将其值设成NULL,从而在析构函数里又试图删除一次。
  Ⅳ:用可能不存在于您的析构函数中的变量调用函数。例如,以下代码可能导致异常因为有可能您在分配内存之前您的对象已经被销毁,或者在应用程序的另一处已经删除了该内存,这样iSomeServer就会处于NULL:
     CMyClass::~CMyClass()
{
iSomeServer->Close();
delete iSomeServer;
}
应该如下编写该代码:
CMyClass::~CMyClass()
{
If(iSomeServer)
{
iSomeServer->Close();
delete iSomeServer;
}
}
  Ⅴ:在NULL指针上调用函数。
Ⅵ:函数调用另一个函数,而其使用的变量已经超出范畴,例如:
    把一个栈变量传送到一个异步函数的回调(callback)里。
     在系统资源不够的情况下,得体的处理失效情况是非常重要的。最受限制的资源通常是系统RAM,因此您需要注意正确的处理内存不足的情况。采用“两端构造方法”和如下所述的CleanupStack机制,对这种防御性编程来说是必不可少和极其重要的。
     对带“R”字头、具备Close()方法的类,总是使用CleanupClosePushL()。这将确保当Leave事件发生时,它们会被恰当的清除。例如:
RFile file;
User::LeaveIfError(file.Open(…));
CleanupClosePushL(file);
CleanupStack::PopAndDestroy(&file);
对用Release()或Destroy()的“R”类,亦可使用CleanupDeletePushL()及CleanupRelasePushL()来取代Close()。
     另外,请记住CleanupStack机制是可扩展的,面对所有Leave事件,都可以用它来有效的清除任何对象。即使您需要处理的是较复杂的情况,也不应该忽略采用正规的清理机制。欲进一步了解TCleanupItem,请参阅Symbian OS Library文档。
     倘若您意图对HBufC变量重新分配资源,在清除它们之后,总是将其设为NULL。由于HBufC的资源分配或其再分配可能会导致Leave事件的发生,从而可能会出现析构函数试图删除已经不存在的HBufC变量的情况。当然,对于任何由堆分配资源的变量而言都应如此,对HBufC变量采取此做法更是已经成为普遍的使用模式。
     当必须采用自己的TRAP时,请忽略所有的报错。常见的编码错误是:
TRAPD(err, DoSomethingL());
If(err == KErrNone || err == KErrNotFound)
{
//Do something else
}
这意味着其他错误码都被忽略。然而,倘若您非用上述模式不可,应采用Leave机制来处理其他错误:
TRAPD(err, DoSomethingL());
If(err == KErrNone||err == KerrNotFound)
{
//Do something else
}
else
User::Leave(err);
 
不要延迟将对象PushL()到CleanupStack上。所有新创建的对象(成员变量除外)应被立即压入该堆栈。例如,下面的做法是错的:
Void doExampleL()
{
CSomeObject* myObject1 = new (ELeave)CSomeObject;
CSomeObject* myObject2 = new (ELeave)CSomeObject;
//Do something here with the variables
CleanupStack::PushL(myObject1);
CleanupStack::PushL(myObject2);
//Do something more with the variables
CleanupStack::PopAndDestroy(2);
//myObject2,myObject1
}
因为myObject2的创建可能失败,造成myObject1”悬”在那里不能被清理。应该这样来实现:
Void doExampleL()
{
CSomeObject* myObject1 = new (ELeave)CSomeObject;
CleanupStack::PushL(myObject1);
CSomeObject* myObject2 = new (ELeave)CSomeObject;
CleanupStack::PushL(myObject2);
//Do something here with the variables
CleanupStack::PopAndDestroy(2);
//myObject2,myObject1
}
注意,那些名称有大写字母C结尾的函数(例如NewLC())会自动把其对象置于CleanupStack。您不应该自己来将这些对象压入CleanupStack,否则该对象会入栈两次。当您创建非成员变量并为其分配内存时,这些由C结尾的函数很有用。
“两端构造方法”是Symbian OS内存管理的关键部分。基本原则是Symbian OS中的构造函数或析构函数永远不应该发生Leave。倘若一个C++构造函数Leave,构造过程未完成的对象得不到清理,因为还没有生成指针指向该对象。为此,Symbian OS中的构造函数仅将该对象实例化,而后调用该对象的ConstructL()函数,在其中将成员数据实例化。一旦ConstructL()发生Leave,标准的析构函数将被调用来清除所有至此已被成功分配的成员变量。在您的编码中照用这一设计模式来防止内存泄漏,至为关键。当您写每一行代码时,都应该问自己:“这一行代码能否发生Leave?”假如回答为“是”,则应考虑“是否所有资源都将被释放?”。
编码中请勿使用_L()宏---而应使用_LIT()。_L()自Symbian OS V5起已是“不推荐使用”(deprecated),它的问题在于它将调用TPtrC(const TText*)构造函数,该构造函数会调用strlen()函数来计算该字串的长度。虽然这不会带来额外的RAM开销,却会在运行时占用更多CPU周期。相反,宏_LIT()直接创建了一个在编译时就全部实例化的对象,节省了构造TPtrC的CPU开销。当然,您首先应该考虑的是否应该使用硬编码的字符串常量,因为当您将来地方化(localize)您的程序时,这种常量类型的描述符(descriptor)可能需要重新编码(我觉得作者这里写的有问题,_LIT()及_L()定义的是常量字符串,并非描述符,它不能够用描述符的一些通用的方法,只有调用_LIT()和_L()重载的()运算符才会变成const TDesC&类型的描述符,并使TDesC的一些通用方法可用---孙东风注)
当在函数参数中使用描述符(descriptor)时,应缺省使用基类。在大多数情况下,以const TDesC&形式来传递描述符。对可修改的描述符,则应使用TDes&。
当在函数中传递或返回对象时,应确保如果您拥有该对象的所有权,您应负责将其清除!Symbian采取的约定是:函数中的指针表示所有权转移到调用者,而使用引用则表示被传递对象的所有权仍属于原所有者。
Active Objects是Symbian OS的重要特性之一。请仔细研究SDK文档、Symbian Developer NetWork白皮书,以充分理解其工作原理。下面有一些有用的窍门:
Ⅰ:在RunL()内无需使用TRAP()。Active Scheduler本身会TRAP函数RunL()并在其发Leave时调用CActive::RunError()。
Ⅱ:为此,您应实现自己的RunError()函数来处理从RunL()的Leave事件。
Ⅲ:保证RunL()操作尽可能简短。长时间运行的RunL()将阻塞其他Active Objects。
Ⅳ:总是实现DoCancel()函数,总是在AO析构函数中调用Cancel()。
您应尽可能利用Active Object框架机制。对于使用电池供电的设备,在一个循环中紧密不断地进行轮流检测(polling)是极其不适当的,将带来大量耗电。写游戏时,对此尤需特别注意,详情参阅Symbian Developer Network网站的技术文档:
www.symbian.com/developer/techlib/papers/porting_3D_games/XenGames_paper.pdf
ViewSrv 11异常对于繁忙运行的程序(例如游戏)是一个潜在的问题。当您的,或者其他任何程序中的ViewSrv active object不能及时响应View Server时就会导致此种异常。典型的最长回应时间是10-20秒。FAQ-0900有详细解释,FAQ-0920有针对如何避免此类问题的实用技巧。二者均可从www3.symbian.com/faq.nsf网页上的Symbian OS FAQ数据库获取。
您无需使用HBufC::Des()来进入一个HBufC对象。只需采用*操作符来为HBufC对象解除引用(dereference)。这对于向某个接受TDesC&(上文的推荐做法)的函数传递HBufC参数时尤其有用。
21当使用标准的程序.INI文件的功能时(即在您的应用UI类中使用Application()->OpenIniFileLC();API时),确保将版本号信息写入流(stream)中。这样使您能够在未来新版本的程序中建立新的流,意味着即使某个最终用户将来安装您的软件的新版本时,不会因为在旧的.INI文件中找不到正确配置或流时发生异常。
22.在您的程序中实现框架类(framework class)时要小心。应该始终从所提供的平台相关的框架类中继承。例如,对UIQ而言,不要从CQikAppUi继承。所有的应用基类(CQikAppUi、CQikApplication、CQikDocument)添加的功能支持更广的框架范围来保证应用程序正确运行。



  
     确保您的应用程序能够对系统关机事件做出响应。在您的AppUi::HandleCommandL()方法中,必须要对EEikCmdExit(以及任何特定平台相关的事件,例如Series 60上的EAknSoftkeyBack)做出响应。
     要对外来系统事件做出响应。请牢记,您的应用程序在一个多任务电话系统上运行,您需要将注意力集中于刚获得/丢失的事件上,以确保当用户获得一个高优先级的通知时您能够做出正确响应。例如,正打进来的电话会干扰您的应用程序的运行,这时应确保您已保存了系统状态和数据(即:您需要对标准的“背景”事件采取恰当的行动—请参阅SDK)。一般来说,系统框架会处理这个问题,您不需要采取任何特殊行动—但一定要确保您没有妨碍系统框架的正常操作。
     内存处理是Symbian OS需要考虑的一个重要课题。在这一点上,应注意电话有时会不同于模拟器。因此在将您的应用程序呈交给“Symbian 认证签名”进行测试之前,务必确保已经在实际电话设备上测试了您的程序。
     内存堆栈空间有限!应尽可能将对象放到内存堆中,而不要放到栈里。KERN-EXEC 3异常(panic)发生的主要原因之一就是栈的破坏/溢出。
     应用程序发生异常(panic)表明您的代码中一定有错误。以下是一些主要的、常见的错误:
Ⅰ:忘记将非对象成员、被分配到堆的变量加到CleanupStack上。
 Ⅱ:将成员变量放到CleanupStack上—这一点要千万避免(在析构函数中将这些变量删除就可以了)。
 Ⅲ:“重复删除”--例如,没有正确的从CleanupStack上Pop()出已经被销毁的对象,造成CleanupStack以后试图再次删除它。或者使用过一个对象之后将其删除但忘记将其值设成NULL,从而在析构函数里又试图删除一次。
  Ⅳ:用可能不存在于您的析构函数中的变量调用函数。例如,以下代码可能导致异常因为有可能您在分配内存之前您的对象已经被销毁,或者在应用程序的另一处已经删除了该内存,这样iSomeServer就会处于NULL:
     CMyClass::~CMyClass()
{
iSomeServer->Close();
delete iSomeServer;
}
应该如下编写该代码:
CMyClass::~CMyClass()
{
If(iSomeServer)
{
iSomeServer->Close();
delete iSomeServer;
}
}
  Ⅴ:在NULL指针上调用函数。
Ⅵ:函数调用另一个函数,而其使用的变量已经超出范畴,例如:
    把一个栈变量传送到一个异步函数的回调(callback)里。
     在系统资源不够的情况下,得体的处理失效情况是非常重要的。最受限制的资源通常是系统RAM,因此您需要注意正确的处理内存不足的情况。采用“两端构造方法”和如下所述的CleanupStack机制,对这种防御性编程来说是必不可少和极其重要的。
     对带“R”字头、具备Close()方法的类,总是使用CleanupClosePushL()。这将确保当Leave事件发生时,它们会被恰当的清除。例如:
RFile file;
User::LeaveIfError(file.Open(…));
CleanupClosePushL(file);
CleanupStack::PopAndDestroy(&file);
对用Release()或Destroy()的“R”类,亦可使用CleanupDeletePushL()及CleanupRelasePushL()来取代Close()。
     另外,请记住CleanupStack机制是可扩展的,面对所有Leave事件,都可以用它来有效的清除任何对象。即使您需要处理的是较复杂的情况,也不应该忽略采用正规的清理机制。欲进一步了解TCleanupItem,请参阅Symbian OS Library文档。
     倘若您意图对HBufC变量重新分配资源,在清除它们之后,总是将其设为NULL。由于HBufC的资源分配或其再分配可能会导致Leave事件的发生,从而可能会出现析构函数试图删除已经不存在的HBufC变量的情况。当然,对于任何由堆分配资源的变量而言都应如此,对HBufC变量采取此做法更是已经成为普遍的使用模式。
     当必须采用自己的TRAP时,请忽略所有的报错。常见的编码错误是:
TRAPD(err, DoSomethingL());
If(err == KErrNone || err == KErrNotFound)
{
//Do something else
}
这意味着其他错误码都被忽略。然而,倘若您非用上述模式不可,应采用Leave机制来处理其他错误:
TRAPD(err, DoSomethingL());
If(err == KErrNone||err == KerrNotFound)
{
//Do something else
}
else
User::Leave(err);
 
不要延迟将对象PushL()到CleanupStack上。所有新创建的对象(成员变量除外)应被立即压入该堆栈。例如,下面的做法是错的:
Void doExampleL()
{
CSomeObject* myObject1 = new (ELeave)CSomeObject;
CSomeObject* myObject2 = new (ELeave)CSomeObject;
//Do something here with the variables
CleanupStack::PushL(myObject1);
CleanupStack::PushL(myObject2);
//Do something more with the variables
CleanupStack::PopAndDestroy(2);
//myObject2,myObject1
}
因为myObject2的创建可能失败,造成myObject1”悬”在那里不能被清理。应该这样来实现:
Void doExampleL()
{
CSomeObject* myObject1 = new (ELeave)CSomeObject;
CleanupStack::PushL(myObject1);
CSomeObject* myObject2 = new (ELeave)CSomeObject;
CleanupStack::PushL(myObject2);
//Do something here with the variables
CleanupStack::PopAndDestroy(2);
//myObject2,myObject1
}
注意,那些名称有大写字母C结尾的函数(例如NewLC())会自动把其对象置于CleanupStack。您不应该自己来将这些对象压入CleanupStack,否则该对象会入栈两次。当您创建非成员变量并为其分配内存时,这些由C结尾的函数很有用。
“两端构造方法”是Symbian OS内存管理的关键部分。基本原则是Symbian OS中的构造函数或析构函数永远不应该发生Leave。倘若一个C++构造函数Leave,构造过程未完成的对象得不到清理,因为还没有生成指针指向该对象。为此,Symbian OS中的构造函数仅将该对象实例化,而后调用该对象的ConstructL()函数,在其中将成员数据实例化。一旦ConstructL()发生Leave,标准的析构函数将被调用来清除所有至此已被成功分配的成员变量。在您的编码中照用这一设计模式来防止内存泄漏,至为关键。当您写每一行代码时,都应该问自己:“这一行代码能否发生Leave?”假如回答为“是”,则应考虑“是否所有资源都将被释放?”。
编码中请勿使用_L()宏---而应使用_LIT()。_L()自Symbian OS V5起已是“不推荐使用”(deprecated),它的问题在于它将调用TPtrC(const TText*)构造函数,该构造函数会调用strlen()函数来计算该字串的长度。虽然这不会带来额外的RAM开销,却会在运行时占用更多CPU周期。相反,宏_LIT()直接创建了一个在编译时就全部实例化的对象,节省了构造TPtrC的CPU开销。当然,您首先应该考虑的是否应该使用硬编码的字符串常量,因为当您将来地方化(localize)您的程序时,这种常量类型的描述符(descriptor)可能需要重新编码(我觉得作者这里写的有问题,_LIT()及_L()定义的是常量字符串,并非描述符,它不能够用描述符的一些通用的方法,只有调用_LIT()和_L()重载的()运算符才会变成const TDesC&类型的描述符,并使TDesC的一些通用方法可用---孙东风注)
当在函数参数中使用描述符(descriptor)时,应缺省使用基类。在大多数情况下,以const TDesC&形式来传递描述符。对可修改的描述符,则应使用TDes&。
当在函数中传递或返回对象时,应确保如果您拥有该对象的所有权,您应负责将其清除!Symbian采取的约定是:函数中的指针表示所有权转移到调用者,而使用引用则表示被传递对象的所有权仍属于原所有者。
Active Objects是Symbian OS的重要特性之一。请仔细研究SDK文档、Symbian Developer NetWork白皮书,以充分理解其工作原理。下面有一些有用的窍门:
Ⅰ:在RunL()内无需使用TRAP()。Active Scheduler本身会TRAP函数RunL()并在其发Leave时调用CActive::RunError()。
Ⅱ:为此,您应实现自己的RunError()函数来处理从RunL()的Leave事件。
Ⅲ:保证RunL()操作尽可能简短。长时间运行的RunL()将阻塞其他Active Objects。
Ⅳ:总是实现DoCancel()函数,总是在AO析构函数中调用Cancel()。
您应尽可能利用Active Object框架机制。对于使用电池供电的设备,在一个循环中紧密不断地进行轮流检测(polling)是极其不适当的,将带来大量耗电。写游戏时,对此尤需特别注意,详情参阅Symbian Developer Network网站的技术文档:
www.symbian.com/developer/techlib/papers/porting_3D_games/XenGames_paper.pdf
ViewSrv 11异常对于繁忙运行的程序(例如游戏)是一个潜在的问题。当您的,或者其他任何程序中的ViewSrv active object不能及时响应View Server时就会导致此种异常。典型的最长回应时间是10-20秒。FAQ-0900有详细解释,FAQ-0920有针对如何避免此类问题的实用技巧。二者均可从www3.symbian.com/faq.nsf网页上的Symbian OS FAQ数据库获取。
您无需使用HBufC::Des()来进入一个HBufC对象。只需采用*操作符来为HBufC对象解除引用(dereference)。这对于向某个接受TDesC&(上文的推荐做法)的函数传递HBufC参数时尤其有用。
21当使用标准的程序.INI文件的功能时(即在您的应用UI类中使用Application()->OpenIniFileLC();API时),确保将版本号信息写入流(stream)中。这样使您能够在未来新版本的程序中建立新的流,意味着即使某个最终用户将来安装您的软件的新版本时,不会因为在旧的.INI文件中找不到正确配置或流时发生异常。
22.在您的程序中实现框架类(framework class)时要小心。应该始终从所提供的平台相关的框架类中继承。例如,对UIQ而言,不要从CQikAppUi继承。所有的应用基类(CQikAppUi、CQikApplication、CQikDocument)添加的功能支持更广的框架范围来保证应用程序正确运行。



  
     确保您的应用程序能够对系统关机事件做出响应。在您的AppUi::HandleCommandL()方法中,必须要对EEikCmdExit(以及任何特定平台相关的事件,例如Series 60上的EAknSoftkeyBack)做出响应。
     要对外来系统事件做出响应。请牢记,您的应用程序在一个多任务电话系统上运行,您需要将注意力集中于刚获得/丢失的事件上,以确保当用户获得一个高优先级的通知时您能够做出正确响应。例如,正打进来的电话会干扰您的应用程序的运行,这时应确保您已保存了系统状态和数据(即:您需要对标准的“背景”事件采取恰当的行动—请参阅SDK)。一般来说,系统框架会处理这个问题,您不需要采取任何特殊行动—但一定要确保您没有妨碍系统框架的正常操作。
     内存处理是Symbian OS需要考虑的一个重要课题。在这一点上,应注意电话有时会不同于模拟器。因此在将您的应用程序呈交给“Symbian 认证签名”进行测试之前,务必确保已经在实际电话设备上测试了您的程序。
     内存堆栈空间有限!应尽可能将对象放到内存堆中,而不要放到栈里。KERN-EXEC 3异常(panic)发生的主要原因之一就是栈的破坏/溢出。
     应用程序发生异常(panic)表明您的代码中一定有错误。以下是一些主要的、常见的错误:
Ⅰ:忘记将非对象成员、被分配到堆的变量加到CleanupStack上。
 Ⅱ:将成员变量放到CleanupStack上—这一点要千万避免(在析构函数中将这些变量删除就可以了)。
 Ⅲ:“重复删除”--例如,没有正确的从CleanupStack上Pop()出已经被销毁的对象,造成CleanupStack以后试图再次删除它。或者使用过一个对象之后将其删除但忘记将其值设成NULL,从而在析构函数里又试图删除一次。
  Ⅳ:用可能不存在于您的析构函数中的变量调用函数。例如,以下代码可能导致异常因为有可能您在分配内存之前您的对象已经被销毁,或者在应用程序的另一处已经删除了该内存,这样iSomeServer就会处于NULL:
     CMyClass::~CMyClass()
{
iSomeServer->Close();
delete iSomeServer;
}
应该如下编写该代码:
CMyClass::~CMyClass()
{
If(iSomeServer)
{
iSomeServer->Close();
delete iSomeServer;
}
}
  Ⅴ:在NULL指针上调用函数。
Ⅵ:函数调用另一个函数,而其使用的变量已经超出范畴,例如:
    把一个栈变量传送到一个异步函数的回调(callback)里。
     在系统资源不够的情况下,得体的处理失效情况是非常重要的。最受限制的资源通常是系统RAM,因此您需要注意正确的处理内存不足的情况。采用“两端构造方法”和如下所述的CleanupStack机制,对这种防御性编程来说是必不可少和极其重要的。
     对带“R”字头、具备Close()方法的类,总是使用CleanupClosePushL()。这将确保当Leave事件发生时,它们会被恰当的清除。例如:
RFile file;
User::LeaveIfError(file.Open(…));
CleanupClosePushL(file);
CleanupStack::PopAndDestroy(&file);
对用Release()或Destroy()的“R”类,亦可使用CleanupDeletePushL()及CleanupRelasePushL()来取代Close()。
     另外,请记住CleanupStack机制是可扩展的,面对所有Leave事件,都可以用它来有效的清除任何对象。即使您需要处理的是较复杂的情况,也不应该忽略采用正规的清理机制。欲进一步了解TCleanupItem,请参阅Symbian OS Library文档。
     倘若您意图对HBufC变量重新分配资源,在清除它们之后,总是将其设为NULL。由于HBufC的资源分配或其再分配可能会导致Leave事件的发生,从而可能会出现析构函数试图删除已经不存在的HBufC变量的情况。当然,对于任何由堆分配资源的变量而言都应如此,对HBufC变量采取此做法更是已经成为普遍的使用模式。
     当必须采用自己的TRAP时,请忽略所有的报错。常见的编码错误是:
TRAPD(err, DoSomethingL());
If(err == KErrNone || err == KErrNotFound)
{
//Do something else
}
这意味着其他错误码都被忽略。然而,倘若您非用上述模式不可,应采用Leave机制来处理其他错误:
TRAPD(err, DoSomethingL());
If(err == KErrNone||err == KerrNotFound)
{
//Do something else
}
else
User::Leave(err);
 
不要延迟将对象PushL()到CleanupStack上。所有新创建的对象(成员变量除外)应被立即压入该堆栈。例如,下面的做法是错的:
Void doExampleL()
{
CSomeObject* myObject1 = new (ELeave)CSomeObject;
CSomeObject* myObject2 = new (ELeave)CSomeObject;
//Do something here with the variables
CleanupStack::PushL(myObject1);
CleanupStack::PushL(myObject2);
//Do something more with the variables
CleanupStack::PopAndDestroy(2);
//myObject2,myObject1
}
因为myObject2的创建可能失败,造成myObject1”悬”在那里不能被清理。应该这样来实现:
Void doExampleL()
{
CSomeObject* myObject1 = new (ELeave)CSomeObject;
CleanupStack::PushL(myObject1);
CSomeObject* myObject2 = new (ELeave)CSomeObject;
CleanupStack::PushL(myObject2);
//Do something here with the variables
CleanupStack::PopAndDestroy(2);
//myObject2,myObject1
}
注意,那些名称有大写字母C结尾的函数(例如NewLC())会自动把其对象置于CleanupStack。您不应该自己来将这些对象压入CleanupStack,否则该对象会入栈两次。当您创建非成员变量并为其分配内存时,这些由C结尾的函数很有用。
“两端构造方法”是Symbian OS内存管理的关键部分。基本原则是Symbian OS中的构造函数或析构函数永远不应该发生Leave。倘若一个C++构造函数Leave,构造过程未完成的对象得不到清理,因为还没有生成指针指向该对象。为此,Symbian OS中的构造函数仅将该对象实例化,而后调用该对象的ConstructL()函数,在其中将成员数据实例化。一旦ConstructL()发生Leave,标准的析构函数将被调用来清除所有至此已被成功分配的成员变量。在您的编码中照用这一设计模式来防止内存泄漏,至为关键。当您写每一行代码时,都应该问自己:“这一行代码能否发生Leave?”假如回答为“是”,则应考虑“是否所有资源都将被释放?”。
编码中请勿使用_L()宏---而应使用_LIT()。_L()自Symbian OS V5起已是“不推荐使用”(deprecated),它的问题在于它将调用TPtrC(const TText*)构造函数,该构造函数会调用strlen()函数来计算该字串的长度。虽然这不会带来额外的RAM开销,却会在运行时占用更多CPU周期。相反,宏_LIT()直接创建了一个在编译时就全部实例化的对象,节省了构造TPtrC的CPU开销。当然,您首先应该考虑的是否应该使用硬编码的字符串常量,因为当您将来地方化(localize)您的程序时,这种常量类型的描述符(descriptor)可能需要重新编码(我觉得作者这里写的有问题,_LIT()及_L()定义的是常量字符串,并非描述符,它不能够用描述符的一些通用的方法,只有调用_LIT()和_L()重载的()运算符才会变成const TDesC&类型的描述符,并使TDesC的一些通用方法可用---孙东风注)
当在函数参数中使用描述符(descriptor)时,应缺省使用基类。在大多数情况下,以const TDesC&形式来传递描述符。对可修改的描述符,则应使用TDes&。
当在函数中传递或返回对象时,应确保如果您拥有该对象的所有权,您应负责将其清除!Symbian采取的约定是:函数中的指针表示所有权转移到调用者,而使用引用则表示被传递对象的所有权仍属于原所有者。
Active Objects是Symbian OS的重要特性之一。请仔细研究SDK文档、Symbian Developer NetWork白皮书,以充分理解其工作原理。下面有一些有用的窍门:
Ⅰ:在RunL()内无需使用TRAP()。Active Scheduler本身会TRAP函数RunL()并在其发Leave时调用CActive::RunError()。
Ⅱ:为此,您应实现自己的RunError()函数来处理从RunL()的Leave事件。
Ⅲ:保证RunL()操作尽可能简短。长时间运行的RunL()将阻塞其他Active Objects。
Ⅳ:总是实现DoCancel()函数,总是在AO析构函数中调用Cancel()。
您应尽可能利用Active Object框架机制。对于使用电池供电的设备,在一个循环中紧密不断地进行轮流检测(polling)是极其不适当的,将带来大量耗电。写游戏时,对此尤需特别注意,详情参阅Symbian Developer Network网站的技术文档:
www.symbian.com/developer/techlib/papers/porting_3D_games/XenGames_paper.pdf
ViewSrv 11异常对于繁忙运行的程序(例如游戏)是一个潜在的问题。当您的,或者其他任何程序中的ViewSrv active object不能及时响应View Server时就会导致此种异常。典型的最长回应时间是10-20秒。FAQ-0900有详细解释,FAQ-0920有针对如何避免此类问题的实用技巧。二者均可从www3.symbian.com/faq.nsf网页上的Symbian OS FAQ数据库获取。
您无需使用HBufC::Des()来进入一个HBufC对象。只需采用*操作符来为HBufC对象解除引用(dereference)。这对于向某个接受TDesC&(上文的推荐做法)的函数传递HBufC参数时尤其有用。
21当使用标准的程序.INI文件的功能时(即在您的应用UI类中使用Application()->OpenIniFileLC();API时),确保将版本号信息写入流(stream)中。这样使您能够在未来新版本的程序中建立新的流,意味着即使某个最终用户将来安装您的软件的新版本时,不会因为在旧的.INI文件中找不到正确配置或流时发生异常。
22.在您的程序中实现框架类(framework class)时要小心。应该始终从所提供的平台相关的框架类中继承。例如,对UIQ而言,不要从CQikAppUi继承。所有的应用基类(CQikAppUi、CQikApplication、CQikDocument)添加的功能支持更广的框架范围来保证应用程序正确运行。



  
     确保您的应用程序能够对系统关机事件做出响应。在您的AppUi::HandleCommandL()方法中,必须要对EEikCmdExit(以及任何特定平台相关的事件,例如Series 60上的EAknSoftkeyBack)做出响应。
     要对外来系统事件做出响应。请牢记,您的应用程序在一个多任务电话系统上运行,您需要将注意力集中于刚获得/丢失的事件上,以确保当用户获得一个高优先级的通知时您能够做出正确响应。例如,正打进来的电话会干扰您的应用程序的运行,这时应确保您已保存了系统状态和数据(即:您需要对标准的“背景”事件采取恰当的行动—请参阅SDK)。一般来说,系统框架会处理这个问题,您不需要采取任何特殊行动—但一定要确保您没有妨碍系统框架的正常操作。
     内存处理是Symbian OS需要考虑的一个重要课题。在这一点上,应注意电话有时会不同于模拟器。因此在将您的应用程序呈交给“Symbian 认证签名”进行测试之前,务必确保已经在实际电话设备上测试了您的程序。
     内存堆栈空间有限!应尽可能将对象放到内存堆中,而不要放到栈里。KERN-EXEC 3异常(panic)发生的主要原因之一就是栈的破坏/溢出。
     应用程序发生异常(panic)表明您的代码中一定有错误。以下是一些主要的、常见的错误:
Ⅰ:忘记将非对象成员、被分配到堆的变量加到CleanupStack上。
 Ⅱ:将成员变量放到CleanupStack上—这一点要千万避免(在析构函数中将这些变量删除就可以了)。
 Ⅲ:“重复删除”--例如,没有正确的从CleanupStack上Pop()出已经被销毁的对象,造成CleanupStack以后试图再次删除它。或者使用过一个对象之后将其删除但忘记将其值设成NULL,从而在析构函数里又试图删除一次。
  Ⅳ:用可能不存在于您的析构函数中的变量调用函数。例如,以下代码可能导致异常因为有可能您在分配内存之前您的对象已经被销毁,或者在应用程序的另一处已经删除了该内存,这样iSomeServer就会处于NULL:
     CMyClass::~CMyClass()
{
iSomeServer->Close();
delete iSomeServer;
}
应该如下编写该代码:
CMyClass::~CMyClass()
{
If(iSomeServer)
{
iSomeServer->Close();
delete iSomeServer;
}
}
  Ⅴ:在NULL指针上调用函数。
Ⅵ:函数调用另一个函数,而其使用的变量已经超出范畴,例如:
    把一个栈变量传送到一个异步函数的回调(callback)里。
     在系统资源不够的情况下,得体的处理失效情况是非常重要的。最受限制的资源通常是系统RAM,因此您需要注意正确的处理内存不足的情况。采用“两端构造方法”和如下所述的CleanupStack机制,对这种防御性编程来说是必不可少和极其重要的。
     对带“R”字头、具备Close()方法的类,总是使用CleanupClosePushL()。这将确保当Leave事件发生时,它们会被恰当的清除。例如:
RFile file;
User::LeaveIfError(file.Open(…));
CleanupClosePushL(file);
CleanupStack::PopAndDestroy(&file);
对用Release()或Destroy()的“R”类,亦可使用CleanupDeletePushL()及CleanupRelasePushL()来取代Close()。
     另外,请记住CleanupStack机制是可扩展的,面对所有Leave事件,都可以用它来有效的清除任何对象。即使您需要处理的是较复杂的情况,也不应该忽略采用正规的清理机制。欲进一步了解TCleanupItem,请参阅Symbian OS Library文档。
     倘若您意图对HBufC变量重新分配资源,在清除它们之后,总是将其设为NULL。由于HBufC的资源分配或其再分配可能会导致Leave事件的发生,从而可能会出现析构函数试图删除已经不存在的HBufC变量的情况。当然,对于任何由堆分配资源的变量而言都应如此,对HBufC变量采取此做法更是已经成为普遍的使用模式。
     当必须采用自己的TRAP时,请忽略所有的报错。常见的编码错误是:
TRAPD(err, DoSomethingL());
If(err == KErrNone || err == KErrNotFound)
{
//Do something else
}
这意味着其他错误码都被忽略。然而,倘若您非用上述模式不可,应采用Leave机制来处理其他错误:
TRAPD(err, DoSomethingL());
If(err == KErrNone||err == KerrNotFound)
{
//Do something else
}
else
User::Leave(err);
 
不要延迟将对象PushL()到CleanupStack上。所有新创建的对象(成员变量除外)应被立即压入该堆栈。例如,下面的做法是错的:
Void doExampleL()
{
CSomeObject* myObject1 = new (ELeave)CSomeObject;
CSomeObject* myObject2 = new (ELeave)CSomeObject;
//Do something here with the variables
CleanupStack::PushL(myObject1);
CleanupStack::PushL(myObject2);
//Do something more with the variables
CleanupStack::PopAndDestroy(2);
//myObject2,myObject1
}
因为myObject2的创建可能失败,造成myObject1”悬”在那里不能被清理。应该这样来实现:
Void doExampleL()
{
CSomeObject* myObject1 = new (ELeave)CSomeObject;
CleanupStack::PushL(myObject1);
CSomeObject* myObject2 = new (ELeave)CSomeObject;
CleanupStack::PushL(myObject2);
//Do something here with the variables
CleanupStack::PopAndDestroy(2);
//myObject2,myObject1
}
注意,那些名称有大写字母C结尾的函数(例如NewLC())会自动把其对象置于CleanupStack。您不应该自己来将这些对象压入CleanupStack,否则该对象会入栈两次。当您创建非成员变量并为其分配内存时,这些由C结尾的函数很有用。
“两端构造方法”是Symbian OS内存管理的关键部分。基本原则是Symbian OS中的构造函数或析构函数永远不应该发生Leave。倘若一个C++构造函数Leave,构造过程未完成的对象得不到清理,因为还没有生成指针指向该对象。为此,Symbian OS中的构造函数仅将该对象实例化,而后调用该对象的ConstructL()函数,在其中将成员数据实例化。一旦ConstructL()发生Leave,标准的析构函数将被调用来清除所有至此已被成功分配的成员变量。在您的编码中照用这一设计模式来防止内存泄漏,至为关键。当您写每一行代码时,都应该问自己:“这一行代码能否发生Leave?”假如回答为“是”,则应考虑“是否所有资源都将被释放?”。
编码中请勿使用_L()宏---而应使用_LIT()。_L()自Symbian OS V5起已是“不推荐使用”(deprecated),它的问题在于它将调用TPtrC(const TText*)构造函数,该构造函数会调用strlen()函数来计算该字串的长度。虽然这不会带来额外的RAM开销,却会在运行时占用更多CPU周期。相反,宏_LIT()直接创建了一个在编译时就全部实例化的对象,节省了构造TPtrC的CPU开销。当然,您首先应该考虑的是否应该使用硬编码的字符串常量,因为当您将来地方化(localize)您的程序时,这种常量类型的描述符(descriptor)可能需要重新编码(我觉得作者这里写的有问题,_LIT()及_L()定义的是常量字符串,并非描述符,它不能够用描述符的一些通用的方法,只有调用_LIT()和_L()重载的()运算符才会变成const TDesC&类型的描述符,并使TDesC的一些通用方法可用---孙东风注)
当在函数参数中使用描述符(descriptor)时,应缺省使用基类。在大多数情况下,以const TDesC&形式来传递描述符。对可修改的描述符,则应使用TDes&。
当在函数中传递或返回对象时,应确保如果您拥有该对象的所有权,您应负责将其清除!Symbian采取的约定是:函数中的指针表示所有权转移到调用者,而使用引用则表示被传递对象的所有权仍属于原所有者。
Active Objects是Symbian OS的重要特性之一。请仔细研究SDK文档、Symbian Developer NetWork白皮书,以充分理解其工作原理。下面有一些有用的窍门:
Ⅰ:在RunL()内无需使用TRAP()。Active Scheduler本身会TRAP函数RunL()并在其发Leave时调用CActive::RunError()。
Ⅱ:为此,您应实现自己的RunError()函数来处理从RunL()的Leave事件。
Ⅲ:保证RunL()操作尽可能简短。长时间运行的RunL()将阻塞其他Active Objects。
Ⅳ:总是实现DoCancel()函数,总是在AO析构函数中调用Cancel()。
您应尽可能利用Active Object框架机制。对于使用电池供电的设备,在一个循环中紧密不断地进行轮流检测(polling)是极其不适当的,将带来大量耗电。写游戏时,对此尤需特别注意,详情参阅Symbian Developer Network网站的技术文档:
www.symbian.com/developer/techlib/papers/porting_3D_games/XenGames_paper.pdf
ViewSrv 11异常对于繁忙运行的程序(例如游戏)是一个潜在的问题。当您的,或者其他任何程序中的ViewSrv active object不能及时响应View Server时就会导致此种异常。典型的最长回应时间是10-20秒。FAQ-0900有详细解释,FAQ-0920有针对如何避免此类问题的实用技巧。二者均可从www3.symbian.com/faq.nsf网页上的Symbian OS FAQ数据库获取。
您无需使用HBufC::Des()来进入一个HBufC对象。只需采用*操作符来为HBufC对象解除引用(dereference)。这对于向某个接受TDesC&(上文的推荐做法)的函数传递HBufC参数时尤其有用。
21当使用标准的程序.INI文件的功能时(即在您的应用UI类中使用Application()->OpenIniFileLC();API时),确保将版本号信息写入流(stream)中。这样使您能够在未来新版本的程序中建立新的流,意味着即使某个最终用户将来安装您的软件的新版本时,不会因为在旧的.INI文件中找不到正确配置或流时发生异常。
22.在您的程序中实现框架类(framework class)时要小心。应该始终从所提供的平台相关的框架类中继承。例如,对UIQ而言,不要从CQikAppUi继承。所有的应用基类(CQikAppUi、CQikApplication、CQikDocument)添加的功能支持更广的框架范围来保证应用程序正确运行。



【事件触发一致性】研究多智能体网络如何通过分布式事件驱动控制实现有限时间内的共识(Matlab代码实现)内容概要:本文围绕多智能体网络中的事件触发一致性问题,研究如何通过分布式事件驱动控制实现有限时间内的共识,并提供了相应的Matlab代码实现方案。文中探讨了事件触发机制在降低通信负担、提升系统效率方面的优势,重点分析了多智能体系统在有限时间收敛的一致性控制策略,涉及系统模型构建、触发条件设计、稳定性与收敛性分析等核心技术环节。此外,文档还展示了该技术在航空航天、电力系统、机器人协同、无人机编队等多个前沿领域的潜在应用,体现了其跨学科的研究价值和工程实用性。; 适合人群:具备一定控制理论基础和Matlab编程能力的研究生、科研人员及从事自动化、智能系统、多智能体协同控制等相关领域的工程技术人员。; 使用场景及目标:①用于理解和实现多智能体系统在有限时间内达成一致的分布式控制方法;②为事件触发控制、分布式优化、协同控制等课题提供算法设计与仿真验证的技术参考;③支撑科研项目开发、学术论文复现及工程原型系统搭建; 阅读建议:建议结合文中提供的Matlab代码进行实践操作,重点关注事件触发条件的设计逻辑与系统收敛性证明之间的关系,同时可延伸至其他应用场景进行二次开发与性能优化。
【四旋翼无人机】具备螺旋桨倾斜机构的全驱动四旋翼无人机:建模与控制研究(Matlab代码、Simulink仿真实现)内容概要:本文围绕具备螺旋桨倾斜机构的全驱动四旋翼无人机展开,重点研究其动力学建模与控制系统设计。通过Matlab代码与Simulink仿真实现,详细阐述了该类无人机的运动学与动力学模型构建过程,分析了螺旋桨倾斜机构如何提升无人机的全向机动能力与姿态控制性能,并设计相应的控制策略以实现稳定飞行与精确轨迹跟踪。文中涵盖了从系统建模、控制器设计到仿真验证的完整流程,突出了全驱动结构相较于传统四旋翼在欠驱动问题上的优势。; 适合人群:具备一定控制理论基础和Matlab/Simulink使用经验的自动化、航空航天及相关专业的研究生、科研人员或无人机开发工程师。; 使用场景及目标:①学习全驱动四旋翼无人机的动力学建模方法;②掌握基于Matlab/Simulink的无人机控制系统设计与仿真技术;③深入理解螺旋桨倾斜机构对飞行性能的影响及其控制实现;④为相关课题研究或工程开发提供可复现的技术参考与代码支持。; 阅读建议:建议读者结合提供的Matlab代码与Simulink模型,逐步跟进文档中的建模与控制设计步骤,动手实践仿真过程,以加深对全驱动无人机控制原理的理解,并可根据实际需求对模型与控制器进行修改与优化。
在当代软件开发领域,Java与Python作为主流编程语言具有显著的技术价值。Java凭借其卓越的跨平台兼容性及严谨的面向对象体系,在商业系统构建中持续发挥核心作用;Python则依托其精炼的语法结构与高效的数据处理库,在机器学习、统计建模等前沿计算领域展现独特优势。 本项目文档系统整理了针对算法训练平台的编程实践内容,重点阐释了如何运用双语言范式解决计算问题。文档体系包含以下核心组成部分: 首先,对各类算法命题进行多维度解析,涵盖基础原理推演、时间复杂度量化比较、内存占用评估等关键技术指标。针对特定问题场景,文档会提供经过优化的数据结构选型方案,并论证不同架构对执行效能的潜在影响。 其次,每个算法案例均配备完整的双语言实现版本。Java实施方案注重类型安全与企业级规范,Python版本则突出代码简洁性与函数式特性。所有示例均包含详尽的执行注释,并附有运行时性能对比数据。 特别需要说明的是,文档中的时序编号体系反映了持续更新的内容组织结构,这种编排方式便于追踪不同阶段的算法实践演进。对于初级开发者,可通过对比两种语言的实现差异深化编程思维;对于资深工程师,则能从中获取系统优化的方法论参考。 在实践应用层面,本文档揭示了理论知识与工程落地的衔接路径:Java方案演示了如何通过合理的数据架构提升分布式系统吞吐量,Python案例则展示了数值计算中算法选择对处理效率的倍增效应。这种跨语言的技术对照,为学术研究与产业实践提供了可复用的设计范式。 通过系统化的算法实践,开发者能够建立完整的计算思维框架,掌握在不同业务场景下进行技术选型的决策依据,最终形成解决复杂工程问题的核心能力。 资源来源于网络分享,仅用于学习交流使用,请勿用于商业,如有侵权请联系我删除!
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值