本教程适用于正在使用FMDB,并期望迁移到WCDB的开发者。

在阅读本教程前,建议先阅读README基础教程

为什么要迁移到WCDB?

WCDB依托于微信上亿用户的实际场景,解决了许多在开发和线上遇到的共性问题,在性能、易用性、功能完整性以及兼容性上都有较好的表现。并且,开发者可以平滑地从FMDB升级到WCDB。

高效

WCDB在并发、ORM以及SQLite源码都做了许多针对性的优化,使得在写入、多线程并发、初始化等方面比FMDB有30%-280%的性能提升

读操作性能对比

从FMDB迁移到WCDB - 图1

写操作性能对比

从FMDB迁移到WCDB - 图2

批量写操作性能对比

从FMDB迁移到WCDB - 图3

多线程读写操作性能对比

从FMDB迁移到WCDB - 图4

初始化性能对比

从FMDB迁移到WCDB - 图5

更多benchmark的数据和测试代码,请参考:性能数据与Benchmark

易用

WCDB通过WINQ和ORM,使得从拼接SQL、获取数据、拼装Object的整个过程,只需要一行代码即可完成

  1. /*
  2. FMDB Code
  3. */
  4. FMResultSet *resultSet = [fmdb executeQuery:@"SELECT * FROM message"];
  5. NSMutableArray<Message *> *messages = [[NSMutableArray alloc] init];
  6. while ([resultSet next]) {
  7. Message *message = [[Message alloc] init];
  8. message.localID = [resultSet intForColumnIndex:0];
  9. message.content = [resultSet stringForColumnIndex:1];
  10. message.createTime = [NSDate dateWithTimeIntervalSince1970:[resultSet doubleForColumnIndex:2]];
  11. message.modifiedTime = [NSDate dateWithTimeIntervalSince1970:[resultSet doubleForColumnIndex:3]];
  12. [messages addObject:message];
  13. }
  1. /*
  2. WCDB Code
  3. */
  4. NSArray<Message *> *messages = [wcdb getAllObjectsOfClass:Message.class fromTable:@"message"];

完整

WCDB基于微信的真实场景,对数据库的常见需求都提供了对应的解决方案,包括:

  • 错误统计
  • 性能统计
  • 损坏修复
  • 反注入
  • 加密

对比与迁移

安装

首先在工程的配置Build Phases->Link Binary With Libraries中,将FMDB以及SQLite的库移出工程。

从FMDB迁移到WCDB - 图6

然后参考安装教程选择适合方式链入WCDB的库。

创建数据库

WCTDatabase通过指定路径进行创建。同时,该接口会自动创建路径中未创建的目录。

  1. NSString* path = @"intermediate/directory/will/be/created/automatically/wcdb";
  2. WCTDatabase* wcdb = [[WCTDatabase alloc] initWithPath:path];

临时数据库可以创建在iOS/macOS的临时目录上

  1. NSString *path = [NSTemporaryDirectory() stringByAppendingPathComponent:@"tmp.db"];
  2. WCTDatabase* wcdb = [[WCTDatabase alloc] initWithPath:path];

WCDB暂不支持创建内存数据库。由于移动平台的磁盘介质大多为SSD,其性能与纯内存操作差别不大。同时内存数据库会占用大量内存,从而导致FOOM。

打开数据库

WCDB会在第一次访问数据库时,自动打开数据库,不需要开发者主动操作。

canOpen接口可用于测试数据库能否正常打开,isOpened接口可用于测试数据库是否已打开。

  1. if (![wcdb canOpen]) {
  2. NSLog(@"open failed");
  3. }
  4.  
  5. if ([wcdb isOpened]) {
  6. NSLog(@"database is already opened");
  7. }

建表与ORM

FMDB不支持ORM,而WCDB可以通过绑定类与表绑定起来,从而大幅度减少代码量。

对于在FMDB已经定义的类:

  1. //Message.h
  2. @interface Message : NSObject
  3.  
  4. @property int localID;
  5. @property(retain) NSString *content;
  6. @property(retain) NSDate *createTime;
  7. @property(retain) NSDate *modifiedTime;
  8.  
  9. @end

和表:

  1. FMDatabase* fmdb = [[FMDatabase alloc] initWithPath:path];
  2. [fmdb executeUpdate:@"CREATE TABLE message(localID INTEGER PRIMARY KEY AUTOINCREMENT, content TEXT, createTime INTEGER, db_modifiedTime INTEGER)"];
  3. [fmdb executeUpdate:@"CREATE INDEX message_index ON message(createTime)"];

可以将其建模为

  1. //Message.h
  2. @interface Message : NSObject <WCTTableCoding>
  3.  
  4. @property int localID;
  5. @property(retain) NSString *content;
  6. @property(retain) NSDate *createTime;
  7. @property(retain) NSDate *modifiedTime;
  8.  
  9. WCDB_PROPERTY(localID)
  10. WCDB_PROPERTY(content)
  11. WCDB_PROPERTY(createTime)
  12. WCDB_PROPERTY(modifiedTime)
  13.  
  14. @end
  15.  
  16. //Message.mm
  17. @implementation Message
  18.  
  19. WCDB_IMPLEMENTATION(Message)
  20. WCDB_SYNTHESIZE(Message, localID)
  21. WCDB_SYNTHESIZE(Message, content)
  22. WCDB_SYNTHESIZE(Message, createTime)
  23. WCDB_SYNTHESIZE_COLUMN(Message, modifiedTime, "db_modifiedTime")
  24.  
  25. WCDB_PRIMARY_AUTO_INCREMENT(Message, localID)
  26. WCDB_INDEX(Message, "_index", createTime)
  27.  
  28. @end

其中:

  • WCDB_IMPLEMENTATION(className)用于定义进行绑定的类
  • WCDB_PROPERTY(propertyName)WCDB_SYNTHESIZE(className, propertyName)用于声明和定义字段。
  • WCDB_SYNTHESIZE(className, propertyName)默认使用属性名作为数据库表的字段名。对于属性名与字段名不同的情况,可以使用WCDB_SYNTHESIZE_COLUMN(className, propertyName, columnName)进行映射。
    • 对于在FMDB已经创建的表,若属性名与字段名不同,则可以用WCDB_SYNTHESIZE_COLUMN宏进行映射,如例子中的db_modifiedTime字段
  • WCDB_PRIMARY_AUTO_INCREMENT(className, propertyName)用于定义主键且自增。
  • WCDB_INDEX(className, indexNameSubfix, propertyName)用于定义索引。
    定义完成后,调用createTableAndIndexesOfName:withClass:即可完成创建。
  1. WCTDatabase* wcdb = [[WCTDatabase alloc] initWithPath:path];
  2. [wcdb createTableAndIndexesOfName:@"message" withClass:Message.class]

注:该接口使用的是IF NOT EXISTS的SQL,因此可以用重复调用。不需要在每次调用前判断表或索引是否已经存在。

更多关于ORM的定义,请参考:ORM使用方法

数据库升级

createTableAndIndexesOfName:withClass:会根据ORM的定义,创建表或索引。

当定义发生变化时,该接口也会对应的增加字段或索引。

因此,该接口可用于数据库表的升级。

  1. //Message.h
  2. @interface Message : NSObject <WCTTableCoding>
  3.  
  4. @property int localID;
  5. @property(assign) const char *newContent;
  6. //@property(retain) NSDate *createTime;
  7. @property(retain) NSDate *modifiedTime;
  8. @property(retain) NSDate *newProperty;
  9.  
  10. WCDB_PROPERTY(localID)
  11. WCDB_PROPERTY(newContent)
  12. //WCDB_PROPERTY(createTime)
  13. WCDB_PROPERTY(modifiedTime)
  14. WCDB_PROPERTY(newProperty)
  15.  
  16. @end
  17.  
  18. //Message.mm
  19. @implementation Message
  20.  
  21. WCDB_IMPLEMENTATION(Message)
  22. WCDB_SYNTHESIZE(Message, localID)
  23. WCDB_SYNTHESIZE_COLUMN(Message, newContent, "content")
  24. //WCDB_SYNTHESIZE(Message, createTime)
  25. WCDB_SYNTHESIZE_COLUMN(Message, modifiedTime, "db_modifiedTime")
  26. WCDB_SYNTHESIZE(Message, newProperty)
  27.  
  28. WCDB_PRIMARY_AUTO_INCREMENT(Message, localID)
  29. WCDB_INDEX(Message, "_index", createTime)
  30. WCDB_UNIQUE(Message, modifiedTime)
  31. WCDB_INDEX(Message, "_newIndex", newProperty)
  32.  
  33. @end
  1. WCTDatabase* db = [[WCTDatabase alloc] initWithPath:path];
  2. [db createTableAndIndexesOfName:@"message" withClass:Message.class]

删除字段

如例子中的createTime字段,删除字段只需直接将ORM中的定义删除即可。

注:由于SQLite不支持删除字段,因此该操作只是将对应字段忽略。

增加字段

如例子中的newProperty字段,增加字段只需直接在ORM定义出添加,并再次调用createTableAndIndexesOfName:withClass:

修改字段

如例子中的newContent字段,字段类型可以直接修改,但需要确保新类型与旧类型兼容;字段名称则需要通过WCDB_SYNTHESIZE_COLUMN(className, proeprtyName, columnName)重新映射到旧字段。

注:由于SQLite不支持修改字段名,因此该操作只是将新的属性映射到原来的字段名。

增加约束

如例子中的WCDB_UNIQUE(Message, modifiedTime),新的约束只需直接在ORM中添加,并再次调用createTableAndIndexesOfName:withClass:

增加索引

如例子中的WCDB_INDEX(Message, "_newIndex", newProperty),新的索引只需直接在ORM添加,并再次调用createTableAndIndexesOfName:withClass:

访问数据库

得益于ORM的定义,开发者无需使用类似intForColumnIndex:的接口手动组装Object。以下是增删查改的代码示例。

查询

  1. /*
  2. FMDB Code
  3. */
  4. FMResultSet *resultSet = [fmdb executeQuery:@"SELECT * FROM message"];
  5. NSMutableArray<Message *> *messages = [[NSMutableArray alloc] init];
  6. while ([resultSet next]) {
  7. Message *message = [[Message alloc] init];
  8. message.localID = [resultSet intForColumnIndex:0];
  9. message.content = [resultSet stringForColumnIndex:1];
  10. message.createTime = [NSDate dateWithTimeIntervalSince1970:[resultSet doubleForColumnIndex:2]];
  11. message.modifiedTime = [NSDate dateWithTimeIntervalSince1970:[resultSet doubleForColumnIndex:3]];
  12. [messages addObject:message];
  13. }
  1. /*
  2. WCDB Code
  3. */
  4. NSArray<Message *> *messages = [wcdb getAllObjectsOfClass:Message.class fromTable:@"message"];

插入

  1. /*
  2. FMDB Code
  3. */
  4. [fmdb executeUpdate:@"INSERT INTO message VALUES(?, ?, ?, ?)", @(message.localID), message.content, @(message.createTime.timeIntervalSince1970), @(message.modifiedTime.timeIntervalSince1970)];
  1. /*
  2. WCDB Code
  3. */
  4. [wcdb insertObject:message into:@"message"];

修改

  1. /*
  2. FMDB Code
  3. */
  4. [fmdb executeUpdate:@"UPDATE message SET modifiedTime=?", @(message.modifiedTime.timeIntervalSince1970)];
  1. /*
  2. WCDB Code
  3. */
  4. [wcdb updateAllRowsInTable:@"message" onProperties:Message.modifiedTime withObject:message];

删除

  1. /*
  2. FMDB Code
  3. */
  4. [fmdb executeUpdate:@"DELETE FROM message"];
  1. /*
  2. WCDB Code
  3. */
  4. [wcdb deleteAllObjects];

条件语句

WCDB通过WINQ完成条件语句,以减轻了拼装SQL的繁琐,并提供一系列优化和反注入等特性。

WINQ和字段映射

对于已定义ORM的类,通过className.propertyName获取数据库字段的映射。

以下是SQL和WINQ之间转换的一些例子。

类型SQL示例WINQ示例
排序ORDER BY localID ASCMessage.localID.order(WCTOrderedAscending)
多字段排序ORDER BY localID ASC, content DESC{Message.localID.order(WCTOrderedAscending), Message.content.order(WCTOrderedDescending)}
聚合函数MAX(localID)Message.localID.max()
条件语句localID==2 AND content IS NOT NULLMessage.localID==2&&Message.content.isNotNull()
多个字段组合localID, content{Message.localID, Message.content}
COUNT()Message.AnyProperty.count()
所有ORM定义的字段(localID, content, createTime, modifiedTime)Message.AllProperties
指定tablemyTable.localIDMessage.localID.inTable("myTable")

改写条件语句

了解了WINQ,就可以完成更复杂的增删查改操作了。

部分查询

  1. /*
  2. FMDB Code
  3. */
  4. NSMutableArray<Message *> *messages = [[NSMutableArray alloc] init];
  5. FMResultSet* resultSet = [fmdb executeQuery:@"SELECT localID, createTime FROM message WHERE localID>=1 OR modified!=createTime"];
  6. while (resultSet && [resultSet next]) {
  7. Message *message = [[Message alloc] init];
  8. message.localID = [resultSet intForColumnIndex:0];
  9. message.createTime = [NSDate dateWithTimeIntervalSince1970:[resultSet doubleForColumnIndex:2]];
  10. [messages addObject:message];
  11. }
  1. /*
  2. WCDB Code
  3. */
  4. NSArray *messages = [wcdb getObjectsOnResults:{Message.localID, Message.createTime}
  5. fromTable:@"message"
  6. where:Message.localID>1||Message.modifiedTime!=Message.createTime];

自增插入

  1. /*
  2. FMDB Code
  3. */
  4. [fmdb executeUpdate:@"INSERT INTO message(localID, content) VALUES(?, ?)", nil, message.content];
  1. /*
  2. WCDB Code
  3. */
  4. message.isAutoIncrement = YES;
  5. [wcdb insertObject:message
  6. onProperties:{Message.localID, Message.content}
  7. into:@"message"];

数值更新

  1. /*
  2. FMDB Code
  3. */
  4. [fmdb executeUpdate:@"UPDATE message SET modifiedTime=? WHERE localID==?", @([NSDate date].timeIntervalSince1970), @(1)];
  1. /*
  2. WCDB Code
  3. */
  4. [wcdb updateRowsInTable:@"message"
  5. onProperty:Message.modifiedTime
  6. withValue:[NSDate date]
  7. where:Message.localID==1];

部分删除

  1. /*
  2. FMDB Code
  3. */
  4. [fmdb executeUpdate:@"DELETE FROM message WHERE localID>0 AND content IS NULL LIMIT ?", @(1)];
  1. /*
  2. WCDB Code
  3. */
  4. [wcdb deleteObjectsFromTable:@"messsage"
  5. where:Message.localID>0&&Message.content!=nil
  6. limit:1];

更多关于增删查改接口的用法,可参考:CRUD教程

特殊语句和核心层接口

WCDB的ObjC层接口封装了绝大部分场景下适用的增删查改语句。但SQL千变万化,接口层不可能覆盖全部场景。对于这种情况,可以通过WINQ的核心层接口进行调用。

对于SQL:EXPLAIN QUERY PLAN CREATE TABLE message(localID INTEGER)

  • 找到其对应的sql-stmt,然后通过以WCDB::Statement开头的类进行调用。如例子中,其对应的sql-stmt为WCDB::StatementExplainWCDB::StatementCreateTable

  • 获取字段映射。对于已经定义ORM的字段,可以通过className.propertyName获取,如:Message.localID。对于未定义ORM的字段,可以通过WCDB::Column columnName("columnName")创建,如 WCDB::Column localID("localID")

  • 根据Statement内的定义,按照与SQL同名的函数调用获得完整的WINQ语句。如例子中,其对应的WINQ语句为

  1. WCDB::ColumnDefList columnDefList = {WCTSampleORM.identifier.def(WCTColumnTypeInteger32, true)};
  2. WCDB::StatementExplain statementExplain = WCDB::StatementExplain().explainQueryPlan(WCDB::StatementCreateTable().create("message", columnDefList));
  • 通过getDescription()打印log,调试确保SQL正确
  1. NSLog(@"SQL: %s", statementExplain.getDescription().c_str());

执行WINQ

通过exec:执行WINQ statement。

  1. [wcdb exec:statement];

获取WINQ运行结果

通过prepare:运行WINQ statement,获得WCTStatement,并以此获取返回值。

  1. WCTStatement *statement = [wcdb prepare:statementExplain];
  2. if (statement && [statement step]) {
  3. for (int i = 0; i < [statement getCount]; ++i) {
  4. NSString *columnName = [statement getNameAtIndex:i];
  5. WCTValue *value = [statement getValueAtIndex:i];
  6. NSLog(@"%@:%@", columnName, value);
  7. }
  8. }

该接口风格与FMDB类似。

更多关于示例代码,可以参考核心层接口

事务

WCDB的基础事务接口与FMDB的接口类似。

  1. /*
  2. FMDB Code
  3. */
  4. BOOL result = [fmdb beginTransaction];
  5. if (!result) {
  6. //failed
  7. }
  8. //do sth...
  9. if (![fmdb commit]) {
  10. //failed
  11. [fmdb rollback];
  12. }
  1. /*
  2. WCDB Code
  3. */
  4. BOOL result = [wcdb beginTransaction];
  5. if (!result) {
  6. //failed
  7. }
  8. //do sth...
  9. if (![wcdb commitTransaction]) {
  10. [wcdb rollbackTransaction];
  11. }

便捷事务接口

runTransaction:接口会在commit失败时自动rollback事务。开发者也可以在BLOCK结束时返回YESNO来决定commit或rollback事务,以此减少代码量。

  1. [wcdb runTransaction:^BOOL{
  2. //do sth...
  3. return result;//YES to commit transaction and NO to rollback transaction
  4. }];

多重语句和批处理

WCDB不支持多重语句。多个语句需拆分单独写。

WCDB对于涉及批量操作的接口,都有内置的事务。如createTableAndIndexesOfName:withClass:insertObjects:into:等,这类接口通常不止执行一条SQL,因此WCDB会自动嵌入事务,以提高性能。

线程安全与并发

FMDB通过FMDatabasePool完成多线程任务。

而对于WCDB,WCTDatabaseWCTTableWCTTransaction的所有SQL操作接口都是线程安全,并且自动管理并发的。

WCDB的连接池会根据数据库访问所在的线程、是否处于事务、并发状态等,自动分发合适的SQLite连接进行操作,并在完成后回收以供下一次再利用。

因此,开发者既不需要使用一个新的类来完成多线程任务,也不需要过多关注线程安全的问题。同时,还能获得更高的性能表现。

  1. /*
  2. FMDB Code
  3. */
  4. //thread-1 read
  5. dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
  6. [fmdbPool inDatabase:^(FMDatabase *_Nonnull db) {
  7. NSMutableArray *messages = [[NSMutableArray alloc] init];
  8. FMResultSet *resultSet = [db executeQuery:@"SELECT * FROM message"];
  9. while ([resultSet next]) {
  10. Message *message = [[Message alloc] init];
  11. message.localID = [resultSet intForColumnIndex:0];
  12. message.content = [resultSet stringForColumnIndex:1];
  13. message.createTime = [NSDate dateWithTimeIntervalSince1970:[resultSet doubleForColumnIndex:2]];
  14. message.modifiedTime = [NSDate dateWithTimeIntervalSince1970:[resultSet doubleForColumnIndex:3]];
  15. [messages addObject:message];
  16. }
  17. //...
  18. }];
  19. });
  20. //thread-2 write
  21. dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
  22. [fmdbPool inDatabase:^(FMDatabase *_Nonnull db) {
  23. [db beginTransaction]
  24. for (Message *message in messages) {
  25. [db executeUpdate:@"INSERT INTO message VALUES(?, ?, ?, ?)", @(message.localID), message.content, @(message.createTime.timeIntervalSince1970), @(message.modifiedTime.timeIntervalSince1970)];
  26. }
  27. if (![db commit]) {
  28. [db rollback];
  29. }
  30. }];
  31. });
  1. /*
  2. WCDB Code
  3. */
  4. //thread-1 read
  5. dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
  6. NSArray *messages = [wcdb getAllObjectsOfClass:Message.class fromTable:@"message"];
  7. //...
  8. });
  9. //thread-2 write
  10. dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
  11. [wcdb insertObjects:messages into:@"message"];
  12. });

配置

在使用数据库时,通常会对其设置一些默认的配置,如cache_sizejournal_mode等。

FMDB通过FMDatabasePoolDelegate进行配置,但其只能在SQLite Handle创建时进行配置。对于已经产生的SQLite handle,很难再次更改配置。

而WCDB可以随时灵活地对其设置或变更。

  1. /*
  2. FMDB Code
  3. */
  4. - (BOOL)databasePool:(FMDatabasePool *)pool shouldAddDatabaseToPool:(FMDatabase *)database
  5. {
  6. FMResultSet* resultSet = [database executeQuery:@"PRAGMA cache_size=-2000"];
  7. [result next];
  8. }
  1. /*
  2. WCDB Code
  3. */
  4. [wcdb setConfig:^BOOL(std::shared_ptr<WCDB::Handle> handle, WCDB::Error &error) {
  5. return handle->exec(WCDB::StatementPragma().pragma(WCDB::Pragma::CacheSize, -2000));
  6. } forName:@"CacheSizeConfig"]'

关闭数据库

关闭数据库通常有两种场景:

  • 数据库使用结束,回收对象。
  • 数据库进行某些操作,需要临时关闭数据库。如移动、复制数据库文件。

回收对象

对于这种情况,开发者无需手动操作。WCDB会自动管理这个过程。对于某一路径的数据库,WCDB会在所有对其的引用释放时,自动关闭数据库,并回收资源。

对于iOS平台,当内存不足时,WCDB会自动关闭空闲的SQLite连接,以节省内存。开发者也可以手动调用[db purgeFreeHandles]对清理单个数据库的空闲SQLite连接。或调用[WCTDatabase PurgeFreeHandlesInAllDatabases]清理所有数据库的空闲SQLite连接。

手动关闭数据库

无论是WCDB的多线程管理,还是FMDB的FMDatabasePool,都存在多线程关闭数据库的问题。

即,当一个线程希望关闭数据库时,另一个线程还在继续执行操作。

而某些特殊的操作需要确保数据库完全关闭,例如移动、重命名、删除数据库等文件层面的操作。

例如,若在A线程进行插入操作的执行过程中,B线程尝试复制数据库,则复制后的新数据库很可能是一个损坏的数据库。

因此,WCDB提供了close:接口确保完全关闭数据库,并阻塞其他线程的访问。

  1. [wcdb close:^(){
  2. //do something on this closed database
  3. }];

隔离Objective-C++代码

WCDB基于WINQ,引入了Objective-C++代码,因此对于所有引入WCDB的源文件,都需要将其后缀.m改为.mm。为减少影响范围,可以通过Objective-C的category特性将其隔离,达到只在model层使用Objective-C++编译,而不影响controller和view

对于已有类WCTSampleAdvance

  1. //WCTSampleAdvance.h
  2. #import <Foundation/Foundation.h>
  3. #import "WCTSampleColumnCoding.h"
  4.  
  5. @interface WCTSampleAdvance : NSObject
  6.  
  7. @property(nonatomic, assign) int intValue;
  8. @property(nonatomic, retain) WCTSampleColumnCoding *columnCoding;
  9.  
  10. @end
  11.  
  12. //WCTSampleAdvance.mm
  13. @implementation WCTSampleAdvance
  14.  
  15. @end

可以创建WCTSampleAdvance (WCTTableCoding)专门用于定义ORM。

为简化定义代码,WCDB同样提供了文件模版

WCTTableCoding文件模版

为了简化定义,WCDB同样提供了Xcode文件模版来创建WCTTableCoding的category。

  • 首先需要安装文件模版。

    • 安装脚本集成在WCDB的编译脚本中,只需编译一次WCDB,就会自动安装文件模版。
    • 也可以手动运行cd path-to-your-wcdb-dir/objc/templates; sh install.sh;手动安装 文件模版
  • 安装完成后重启Xcode,选择新建文件,滚到窗口底部,即可看到对应的文件模版。

从FMDB迁移到WCDB - 图7

  • 选择WCTTableCoding从FMDB迁移到WCDB - 图8输入需要实现WCTTableCoding的类

  • 这里以WCTSampleAdvance为例,Xcode会自动创建WCTSampleAdvance+WCTTableCoding.h文件模版:

  1. #import "WCTSampleAdvance.h"
  2. #import <WCDB/WCDB.h>
  3.  
  4. @interface WCTSampleAdvance (WCTTableCoding) <WCTTableCoding>
  5.  
  6. WCDB_PROPERTY(<#property1 #>)
  7. WCDB_PROPERTY(<#property2 #>)
  8. WCDB_PROPERTY(<#property3 #>)
  9. WCDB_PROPERTY(<#property4 #>)
  10. WCDB_PROPERTY(<#... #>)
  11.  
  12. @end
  • 加上类的ORM实现即可。
  1. //WCTSampleAdvance.h
  2. #import <Foundation/Foundation.h>
  3. #import "WCTSampleColumnCoding.h"
  4.  
  5. @interface WCTSampleAdvance : NSObject
  6.  
  7. @property(nonatomic, assign) int intValue;
  8. @property(nonatomic, retain) WCTSampleColumnCoding *columnCoding;
  9.  
  10. @end
  11.  
  12. //WCTSampleAdvance.mm
  13. @implementation WCTSampleAdvance
  14.  
  15. WCDB_IMPLEMENTATION(WCTSampleAdvance)
  16. WCDB_SYNTHESIZE(WCTSampleAdvance, intValue)
  17. WCDB_SYNTHESIZE(WCTSampleAdvance, columnCoding)
  18.  
  19. WCDB_PRIMARY_ASC_AUTO_INCREMENT(WCTSampleAdvance, intValue)
  20.  
  21. @end
  22.  
  23. //WCTSampleAdvance+WCTTableCoding.h
  24. #import "WCTSampleAdvance.h"
  25. #import <WCDB/WCDB.h>
  26.  
  27. @interface WCTSampleAdvance (WCTTableCoding) <WCTTableCoding>
  28.  
  29. WCDB_PROPERTY(intValue)
  30. WCDB_PROPERTY(columnCoding)
  31.  
  32. @end

此时,原来的WCTSampleAdvance.h中不包含任何C++的代码。因此,其他文件对其引用时,不需要修改文件名后缀。只有Model层需要使用WCDB接口的类,才需要包含WCTSampleAdvance+WCTTableCoding.h,并修改文件名后缀为.mm

示例代码请参考:WCTSampleAdvanceWCTSampleNoObjectiveCpp

更多教程和示例代码,请参考WCDB的wikisample