RSS 2.0 Feed
2007-03 Entries
摘要:RArray类,属于symbian OS提供的基础容器类,并且是比较重要和常用的一个。 如果从名字来看这是一个数据类,功能貌似和 MFC的CArray,stl的vector差不多吧?如果这么想就错了,RArray是个泛型数组容器类,但是功能比CArray vector 提供的要多。 尤其是他提供的排序和查找功能,其实现方法比较诡异,对初学者来说容易造成迷惑。个人认为这个设计比较蹩脚,至于为什么会这样,我想不出,已经写信给作者咨询了,不过还没得到答复。 RArray的排序是这样的,它规定数组中每个元素可以有一个 order key,这个order key其实就是数组元素的某4个字节至于具体是哪4个字节作为order key是由key offset决定的,key offset 是构造函数的参数,是可选的,默认为0 就是说,默认情况下,数组里面每个元素的前4个字节会被用来作为排序的key。因此RArray也规定,每个元素至少要有4个字节,并且大小必须是4个倍数,否则某些函数不能用,甚至会产生异常。 比如我们声明一个 结构体 Bar 作为数组的元素,然后构建一个Bar的数组 barArray struct Bar{   TInt  iId;   TInt  iSize;} RArray<Bar> barArray; 于是 iId就会成为order key了,因为默认情况下key offset 为0,iId就是元素的头4个字节。如果你要用isize做order key,那么只要指定key offset为4就好了。 如果需要RArray的排序功能那么 你就需要用到一系列名字中带有KeyOrder的函数比如 InsertInSignedKeyOrderL,FindInSignedKeyOrderL等等 Insert操作时,会按照插入元素的order key排序来查找合适的Insert位置。Find时,也是比较order key,由于查找时只比较order key,所以会写出这样有点诡异的代码比如我们要查找 iId 为 100的元素的位置Bar foo;foo.iId = 100;TInt index = barArray.InsertInSignedKeyOrderL(foo);查找条件中的 iSize没有初值?对没有,也没必要,因为InsertInSignedKeyOrderL只比较前4个字节iSize用不到,所以也不用给初值。 注意 函数名中的 Signed,还有与之对应的 Unsigned,这些表示比较order key的时候是否考虑符号。就是order key是 当作TInt还是TUint来比较。 如果你不用这些带有KeyOrder的系列函数,那么和一个CArray的数组基本一样。 另外 RArray有2个特化版本,RArray<TInt>,RArray<TUint>,可能是为了提高效率吧。 现在我明白Symbian 里为什么没有 list map 等容器类了,RArray实在是一专多能。不过我还是认为这样的设计不够优雅,也许作者有苦衷?希望RArray作者的答复里可以给我解释。...[阅读全文]

posted @ | Feedback (25) | Filed Under [ 随便聊聊 ]

摘要:Windows的庞大和臃肿是早有体会的了,最近作Symbian开发 别以为Symbian是个小东西,我得到的统计Symbian & s60 源代码包括 10万个左右文件,总共1G左右的大小。但这还不是全部源代码,某些内核,本地化资源的内容没有计算在内。 在手机和PDA上系统的Release版本也不过几M的样子,源代码是Release的几百倍。由此推算windows的源代码要以TB计算了。 在现在的软件开发和设计模式下,代码膨胀是不可避免的,能写出简练精巧的代码的人是极少数,其他大部分是写臃肿丑陋的代码的庸才,但是需要实现功能那么多,靠少数高手是根本干不完的。 另外需求变化很快,新的需求出现,原有的设计通常无法满足,只有在上面修修补补,经常可以看到代码中无数复杂嵌套的If ...else... ,重构的代价是高昂的,尤其在Weekly build 甚至Daily build的压力下,很少会冒险去动原有的可以测试通过的代码。 对于管理者来说,他们很清楚这样下去产品终究会走上绝路,但是他们会极力避免发生在自己的任期上。我有时想,软件产品就像生命一样,也会经历生老病死的轮回。 最后做个推荐,我发现博客堂只有放在首页的随笔才会访问量比较高,文章就差很多了写了篇关于 Symbian 两次构造 NewL NewLC ConstructL 的文章,有兴趣的去看看吧。http://blog.joycode.com/yaodong/articles/94824.aspx...[阅读全文]

posted @ | Feedback (18) | Filed Under [ 随便聊聊 ]

摘要:听说开发Symbian程序,首要的也是最重要的之一,就是搞清楚CleanupStackCleanupStack 用起来貌似很简单,我写了如下代码: 假设我有一个叫 CFoo的类 CFoo *p = new (ELeave) CFoo();CleanupStack::PushL(p);// 使用 p 作一些事情,例如调用 p->SomeFunctionL();// ....CleanupStack::Pop();delete p; 我不禁疑问为什么要这样?Windows程序从来没有这样的代码,DOS都没有,Linux貌似也没有。Symbian这样做是为什么?相信所有开始学习Symbian的人都会有这样的疑问。 我看了一下Symbian OS的一些介绍和历史,了解到Symbian OS从开始就是针对移动设备,主要是手机PDA所设计开发的操作系统,设计上对于资源消耗,效率和安全稳定性都比较看重。移动设备的主要特点就是,运算能力差,内存小,需要长时间运行,有时需要长达N年。Symbian还是一个多任务系统,多个进程共享系统内存。Symbian还是一个开放的平台,提供开发工具和接口,允许任何人为它开发软件。 在这样的情况下,内存泄漏是一种需要极力避免的错误,由于运行时间长,哪怕一点点内存泄漏也会随着时间增长吞噬掉系统的本来就不多的内存,从而造成系统崩溃。 =====================回忆的分割线========================================== 我回顾了一下微软体系下是怎么解决内存泄漏的 C语言时代,完全没有解决办法,一切都靠程序员自己提高警惕,小心处理所有的内存分配和释放。那个时代,高手和一般程序员之间的差别是很大的,能够驾驭大型的程序,小心控制好内存和指针,需要高超的技巧和丰富的经验。好在DOS也不需要太稳定,每天都重新启动N次也无所谓。 C++语言,增加了对象的概念,有结构化异常处理,给程序员提供了一种可以控制内存的方法。我们可以把内存释放放在类的析构函数里,这样就不会被忘掉,也可以用智能指针模板类比如 std::auto_ptr来包装指针。如果程序可能意外退出,那么我们可以用结构化异常处理,捕获异常,我们仍然有机会在程序结束前释放掉内存。我可以用容器类来存放数据,避免自己分配和管理内存。 到了windows时代,有了进程地址空间的概念,即使一个进程意外结束,即使有大量的内存泄漏,当进程结束后所有这个进程的内存都会被释放掉,甚至包括进程用到的内核对象和系统资源都会被释放。而且随着PC硬件的发展,内存变得很大,一些小的内存泄漏几乎不会产生什么问题,在积累到足以导致系统崩溃前,通常进程就结束掉了。 即使如此,内存泄露仍然是个大问题,尤其是对于一些企业级应用,服务性程序,需要长时间运行,内存泄漏的积累会造成大麻烦,所以产生了很多工具和类库,工具可以用来监测内存泄漏,帮助开发者找出问题,类库封装了很多系统服务和内存管理,让开发者避免和内存直接打交道。 只要是人就会出错,总会在某个时候疏忽一下忘记释放。如果成本允许,那么可以靠大量的测试来防止。以上的方法并没有从根本上解决问题,虽然我近几年已经很少出内存泄漏错误了,但也不是绝对没有。 Java的出现,是一个重大转变,delete这个关键字彻底消失了,只有new保留了下来,内存的释放被垃圾回收机制代替了,从此开发人员不用关心内存的释放了,一切都交给系统去处理,后来的C#也是类似的机制。 ===========================回归的分割线======================================== 似乎有点扯远了,回到Symbian,我在想为什么Symbian没有采用以上的方法呢?Symbian本身是使用C++编写的,开发语言也主要是C++,虽然也支持Java和其他一些脚本语言,但是有些程序必须用C++来开发,平台的API几乎都是C++接口的。Java的虚拟机,脚本的解释器总是要用C++来编写的。所以C++是唯一的选择。完全用Java 和 C#来开发肯定不可能。 那么提供一个类库来解决内存分配释放呢?事实上这样的类是有的,但是只能解决开发人员忘记释放的问题,对于程序意外结束仍然没有办法。 那么异常处理呢?很遗憾,一篇文档上说在Symbian开始设计开发的时代,还是上个世纪90年代的事,那时候C++编译器还不能完全支持C++异常处理,至少Symbian 的编译器GCC 不可以。 我估计最后Symbian 的设计者选择了一个看似不够优雅,但是却是无可奈何的选择,那就是CleanupStack。 这个方案基本是异常的机制的替代品,大致是这样的程序创建一个 Stack(栈),分配的内存的指针会被通过调用 CleanupStack::PushL(..) 放到Stack里。释放内存之前 调用 CleanupStack::Pop() 把指针从stack中取出。另外我们经常会看到 TRAP(。。。) 宏,你可以简单地认为相当于 try...catch... ,出现异常的时候会获得错误码。所以要把一些危险的函数调用放到TRAP里面。(如果以后有机会我打算详细写写TRAP) 那么如果在new 和 delete之间产生了异常,那么内存指针就会保留在stack里,系统在进程结束的时候会把Stack里面的内存释放掉。这样保证了即使程序意外结束,也不会有内存泄漏。 这样的方案是不是足够好呢?如果开发人员忘记了PushL或者Pop怎么办?不是一样会有内存泄漏吗?开发人员记性不好的确是个大麻烦,但是有很多工具可以帮你检测问题,比如CodeScanner PC-Lint等等,如果你连使用工具都忘掉了,我就无语了。。。 CodeScanner是个好东西,专门为了Symbian C++开发的静态代码分析工具。可以给出详细的关于代码问题的报告。CodeScanner:http://www.mobileinnovation.co.uk 看来我只要保证了 PushL和Pop匹配,所有的内存分配都会被记录下来,程序结束之前都会被释放掉。...[阅读全文]

posted @ | Feedback (5) | Filed Under [ 随便聊聊 ]

摘要:最近2年生活过的很动荡,主要是家庭的原因,每隔几个月就有些变化,现在总算稳定下来了。和技术也有些疏远,可以看到我的blog 两年来都没什么更新,这里要感谢开心同学没有把我的帐号删掉 最近作了一个重要的决定,就是告别PC & windows开发,转向移动和潜入式平台,比如Symbian.此前几乎没有Symbian 开发经验,一切从头开始,再次回到helloworld,就像我写第一个Turbo C程序那样。虽然Symbian 也是用C++开发,但是却有很大不同。 今后我会写一些学习的感悟,以及经验和技巧与大家分享。 带着我10年的微软平台的开发经验去观察 Symbian 的世界,我尽量用Windows 的概念通过类比,去解释Symbian的问题。相信会对其他从Windows转向Symbian的人有所帮助。...[阅读全文]

posted @ | Feedback (10) | Filed Under [ 随便聊聊 ]