• 为什么要迁移到WCDB?
    • 高效
      • 读操作性能对比
      • 写操作性能对比
      • 批量写操作性能对比
      • 多线程读写操作性能对比
      • 初始化性能对比
  • 易用
  • 完整
  • 对比与迁移
    • 安装
    • 创建数据库
    • 打开数据库
    • 建表与ORM
    • 数据库升级
      • 删除字段
      • 增加字段
      • 修改字段
      • 增加约束
      • 增加索引
  • 访问数据库
    • 查询
    • 插入
    • 修改
    • 删除
  • 条件语句
    • WINQ和字段映射
    • 改写条件语句
      • 部分查询
      • 自增插入
      • 数值更新
      • 部分删除
  • 特殊语句和核心层接口
    • 执行WINQ
    • 获取WINQ运行结果
  • 事务
    • 便捷事务接口
  • 多重语句和批处理
  • 线程安全与并发
  • 配置
  • 关闭数据库
    • 回收对象
    • 手动关闭数据库
  • 隔离Objective-C++代码
    • WCTTableCoding文件模版

    本教程适用于正在使用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

    示例代码请参考:WCTSampleAdvance和WCTSampleNoObjectiveCpp

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