2006/05/26

巨硬II,pDatabase & pRecord

又是一个不眠之夜。

巨硬的代码进度令人满意,虽然不快,但是在MM(MindManager)的帮助下清晰有序的进行着。先前写的一些基本类都派上了用场,对PalmOS Programming的理解也小有收获。

大致说来有这么几个问题:
1) PalmOS的存储方式非常特殊,高效,但不太适合C++封装,用C写怎么都思如涌泉,用C++就经常犯愁:尤其是用指针还是用集合类的问题。
2) CodeWarrior里面自带的POL类库是MFC风格,但有些封装过渡,大量的使用模板,提供RTTI能力,丰富的Stream类,对于大多数Palm风格的小程序来说,这个封装过于臃肿,反而带来不变,譬如弹出式程序就不能使用全局变量,POL的类就根本没办法使用;窗口也不能想弹就弹;截取硬按键也是个麻烦。还有就是对于Record的流式存储,乍一看,形式上很漂亮,但是不符合Storage Heap的数据库本质,密集访问的时候效率也成问题。
3) 自己从POL中抄袭篡改而来的pDatabase/pRecord类,个人认为更符合PalmOS的数据访问要求,提供的Insert,Overwrite,Replace,Erase等直接对Record写入的函数,远比流方便,效率也更高。
4) 数据库程序有个完整性要求,应避免多步更新永久存储数据,所以更新数据时务必要使用DmAttachRecord,而不是使用DmWrite多次写入;除此之外,pRecord类中还包含了一个MemHandle,允许用户多次写入然后一次提交生效,如果在写入过程中程序遇到错误,则不会修改任何存储数据。
5) 这种Commit方式,在不得不同时更新多个记录的时候更加重要,尤其是在遇到低内存的时候,如果依次更新,那可能DmNewHandle都会失败;如果要同时更新多条记录,可以用多个pRecord执行更新操作,然后全部CommitCheck--这个步骤是检查内存Lock情况,如果全部通过就一次全部Commit,虽然不是说绝对不会出问题,但是做到这个地步,出错的可能性和彗星撞地球差不多了吧。
6) PalmOS的数据库信息操作异常烦琐,pDatabase中规划的InitAppInfo通过三次调用才实现了高效封装--这是从POL里学来的。不过封装之后,派生类只要实现虚函数就可以了,用起来还是很方便的。
7) C++中的错误处理机制在PalmOS中不易实现,出了问题Debug也麻烦;所以还是使用了传统的返回Err方式。不过要小心的是构造函数和析构函数是不能返回值的,所以不要在构造函数和析构函数中调用任何可能返回Err的操作,包括打开数据库读写或者申请内存之类,可能后果严重。应该为类提供单独的Init或者Open函数来完成这个要求。
8) 数据库的工作模式很值得探讨。如果为了方便从pDatabase派生或者包含了该数据作为成员,很有可能为了提高速度把一些记录或者AppInfo锁定,但是这样就会给数据库的维护造成麻烦,把数据库的维护函数都写成static并不是好办法,这就走回C的老路上去了。更好的办法是把函数分为两类,一类工作在normal模式下,该模式下可以锁定数据库中的记录;另一类是维护模式,即把所有锁定记录的指针全部release掉,这在pDatabase中通过两个虚函数来实现,一个是InitInternals,一个是DeinitInternals。
9) 从效率出发的话,Normal模式下应该尽量多的锁定和传递指针;而Maintenance模式下,尽量避免使用指针,取而代之使用C++类,是更好的办法,依据这个原则就可以混合C/C++代码,写出既高效又易维护的代码来。
10) 最后一个问题是PalmOS的排序,如果使用C++,没办法传递动态的DmComparF函数给系统,这挺头疼的,不然在pDatabase里写一个Compare虚函数是非常漂亮的;如果非要这样用的话,没办法的办法是写一个Find虚函数,反正最终继承的数据库类知道自己的数据格式,不过这样做给人的感觉总象苹果里的虫子,所以,还是尽可能在数据库设计的时候把排序的Field直接写在Record头上,以避免需要从记录中提取出对象才能比较。

这两个类代码,在写完巨硬II之后好好整理一下,然后可以共享出来大家探讨了。

1 comment:

Anonymous said...

我就一直觉得用C写Palm程序更简单些
对数据库部分主要要注意的是NVFS的bug,即使到新的ROM更新还是一直有很大问题。记录数要少,单条记录大没有关系,就是必须保证总记录数少。