WCDB的使用操作
WCDB 入门教程
目录
WCDB 简介
WCDB (WeChat DataBase) 是微信团队开源的一个高效、完整、易用的移动数据库框架,基于 SQLite 开发,支持 iOS、macOS、Android 等平台。
主要特性
- 易用性:支持 ORM (对象关系映射),通过宏定义即可完成模型类和数据库表的绑定
- 高效性:基于 SQLite 优化,支持多线程并发读写
- 完整性:提供数据库加密、损坏修复、数据迁移等完整解决方案
- 跨平台:iOS/macOS 使用 Objective-C/Swift,Android 使用 Java/Kotlin
环境搭建
1. 使用 CocoaPods 集成
在项目的 Podfile 中添加依赖:
platform :ios, '12.0'
use_frameworks!
target 'WCDB-Test' do
pod 'WCDB.objc', '~> 2.1.0'
end然后执行安装:
pod install注意:安装完成后,后续打开项目需要使用
.xcworkspace文件而不是.xcodeproj
项目结构
WCDB-Test/
├── WCDB-Test/
│ ├── AppDelegate.h/m # 应用代理
│ ├── SceneDelegate.h/m # 场景代理 (iOS 13+)
│ ├── ViewController.h/mm # 主控制器 (数据库操作演示)
│ ├── Sample.h/m # 数据模型类
│ ├── Assets.xcassets/ # 资源文件
│ └── Base.lproj/ # 本地化资源
├── Podfile # CocoaPods 依赖配置
├── Podfile.lock # 锁定依赖版本
├── WCDB-Test.xcodeproj/ # Xcode 项目
└── WCDB-Test.xcworkspace/ # Xcode 工作空间模型类定义
Sample.h
WCDB 通过宏定义实现 ORM 映射,需要在头文件中声明属性和数据库字段:
#import <Foundation/Foundation.h>
#import <WCDBObjc/WCDBObjc.h>
@interface Sample : NSObject <WCTTableCoding>
@property (nonatomic, assign) int identifier;
@property (nonatomic, strong) NSString *content;
// 声明 WCDB 属性,生成对应的类方法
WCDB_PROPERTY(identifier)
WCDB_PROPERTY(content)
@endSample.m
在实现文件中绑定模型类与数据库表:
#import "Sample.h"
@implementation Sample
// 声明实现
WCDB_IMPLEMENTATION(Sample)
// 合成属性,建立属性与数据库字段的映射
WCDB_SYNTHESIZE(identifier)
WCDB_SYNTHESIZE(content)
@end关键宏说明
| 宏 | 作用 |
|---|---|
WCDB_PROPERTY(property) |
在头文件中声明,生成获取属性的类方法 |
WCDB_IMPLEMENTATION(class) |
在实现文件中声明,标识该类参与 WCDB 绑定 |
WCDB_SYNTHESIZE(property) |
合成属性,建立属性与数据库字段的映射 |
数据库操作
1. 初始化数据库
#import "Sample.h"
#import <WCDBObjc/WCDBObjc.h>
@interface ViewController ()
@property (nonatomic, strong) WCTDatabase *database;
@end
@implementation ViewController
- (void)setupDatabase {
// 获取数据库路径 (Document 目录)
NSString *path = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,
NSUserDomainMask, YES) firstObject];
NSString *dbPath = [path stringByAppendingPathComponent:@"sample.db"];
// 创建数据库对象
self.database = [[WCTDatabase alloc] initWithPath:dbPath];
// 创建表
BOOL result = [self.database createTable:@"sampleTable" withClass:Sample.class];
NSLog(@"创建表: %@", result ? @"成功" : @"失败");
}
@end2. 插入数据 (Create)
- (void)insertData {
// 插入 3 条测试数据
for (int i = 1; i <= 3; i++) {
Sample *obj = [[Sample alloc] init];
obj.identifier = (int)[[NSDate date] timeIntervalSince1970] + i;
obj.content = [NSString stringWithFormat:@"test_data_%d", i];
BOOL result = [self.database insertObject:obj intoTable:@"sampleTable"];
NSLog(@"插入第 %d 条: %@", i, result ? @"成功" : @"失败");
}
}3. 查询数据 (Read)
- (void)queryData {
// 查询所有数据
NSArray<Sample *> *objects = [self.database getObjectsOfClass:Sample.class
fromTable:@"sampleTable"];
NSLog(@"查询到 %lu 条数据:", (unsigned long)objects.count);
for (Sample *obj in objects) {
NSLog(@" ID: %d, Content: %@", obj.identifier, obj.content);
}
}4. 更新数据 (Update)
- (void)updateData:(int)targetId {
// 准备更新数据
Sample *updateObject = [[Sample alloc] init];
updateObject.content = @"updated_content";
// 更新指定 ID 的数据
BOOL result = [self.database updateTable:@"sampleTable"
setProperties:[Sample content] // 要更新的字段
toObject:updateObject
where:[Sample identifier] == targetId];
NSLog(@"更新结果: %@", result ? @"成功" : @"失败");
}重要说明:在 Objective-C++ 文件(.mm)中,访问 WCDB 属性时必须使用消息发送语法 [Sample content],不能使用点语法 Sample.content。
5. 删除数据 (Delete)
- (void)deleteData {
// 删除所有数据
BOOL result = [self.database deleteFromTable:@"sampleTable"];
NSLog(@"删除所有数据: %@", result ? @"成功" : @"失败");
}
// 删除指定条件的数据
- (void)deleteDataWithId:(int)targetId {
BOOL result = [self.database deleteFromTable:@"sampleTable"
where:[Sample identifier] == targetId];
NSLog(@"删除 ID=%d 的数据: %@", targetId, result ? @"成功" : @"失败");
}遇到的问题及解决方案
问题 1:Property 'content' not found on object of type 'Sample'
错误信息:
ViewController.m:154:59 Property 'content' not found on object of type 'Sample'问题原因:
WCDB 的 WCDB_PROPERTY 宏生成的是类方法(如 + (WCTProperty *)content),而不是类属性。在 Objective-C++ 文件(.mm)中,使用点语法 Sample.content 访问类方法会导致编译错误。
解决方案:
将点语法改为消息发送语法:
// ❌ 错误写法(在 .mm 文件中)
setProperties:Sample.content
where:Sample.identifier == targetId
// ✅ 正确写法
setProperties:[Sample content]
where:[Sample identifier] == targetId问题 2:Sandbox: rsync deny file-write-create
错误信息:
Sandbox: rsync(32540) deny(1) file-write-create /Users/.../WCDBObjc.framework/_CodeSignature
rsync: mkpathat: Operation not permitted问题原因:
Xcode 15 引入的 ENABLE_USER_SCRIPT_SANDBOXING 功能会限制构建脚本的文件系统访问权限,导致 CocoaPods 的 [CP] Embed Pods Frameworks 脚本无法正常复制和签名 Framework。
解决方案:
在项目的 Build Settings 中,将 ENABLE_USER_SCRIPT_SANDBOXING 设置为 NO:
- 打开项目 → 选择 Target → Build Settings
- 搜索
ENABLE_USER_SCRIPT_SANDBOXING - 将值改为
NO
或者修改 project.pbxproj 文件:
- ENABLE_USER_SCRIPT_SANDBOXING = YES;
+ ENABLE_USER_SCRIPT_SANDBOXING = NO;建议操作:
修改后需要清理构建缓存:
- Xcode:
Product→Clean Build Folder(Cmd+Shift+K) - 或命令行:
rm -rf ~/Library/Developer/Xcode/DerivedData/项目名-*
问题 3:插入 3 条数据但查询出 4 条
问题描述:
代码意图插入 3 条数据,但查询结果显示有 4 条。
问题代码:
- (void)insertData {
// 第 1 条:单独插入
Sample *object = [[Sample alloc] init];
...
[self.database insertObject:object intoTable:@"sampleTable"];
// 第 2-4 条:循环插入 3 条
for (int i = 1; i <= 3; i++) {
Sample *obj = [[Sample alloc] init];
...
[self.database insertObject:obj intoTable:@"sampleTable"];
}
[self log:@"额外插入 3 条测试数据"]; // 日志误导
}问题原因:
代码实际插入了 1 + 3 = 4 条数据,但日志提示"额外插入 3 条",容易让人误解为总共只插入了 3 条。
解决方案:
统一使用循环插入,并修正日志:
- (void)insertData {
[self log:@"\n--- 插入操作 ---"];
// 插入 3 条测试数据
for (int i = 1; i <= 3; i++) {
Sample *obj = [[Sample alloc] init];
obj.identifier = (int)[[NSDate date] timeIntervalSince1970] + i;
obj.content = [NSString stringWithFormat:@"test_data_%d", i];
BOOL result = [self.database insertObject:obj intoTable:@"sampleTable"];
[self log:[NSString stringWithFormat:@"插入第 %d 条: %@", i, result ? @"成功" : @"失败"]];
}
[self log:@"共插入 3 条测试数据"];
}最佳实践
1. 数据库路径选择
- 数据库文件应放在应用的沙盒目录内(如
Documents或Library) - 不要放在
tmp目录,因为系统可能会清理该目录 - 考虑将数据库放在
Library/Application Support下,该目录会被 iTunes 备份
2. 线程安全
- WCDB 是线程安全的,可以在多线程环境中使用
- 数据库操作会在内部队列中异步执行
3. 主键设置
- 建议为模型类设置主键,避免插入重复数据
- 可以使用
WCDB_PRIMARY宏声明主键:
// Sample.h
@interface Sample : NSObject <WCTTableCoding>
@property (nonatomic, assign) int identifier;
@property (nonatomic, strong) NSString *content;
WCDB_PROPERTY(identifier)
WCDB_PROPERTY(content)
WCDB_PRIMARY(identifier) // 声明主键
@end
// Sample.m
WCDB_IMPLEMENTATION(Sample)
WCDB_SYNTHESIZE(identifier)
WCDB_SYNTHESIZE(content)
WCDB_PRIMARY_AUTO_INCREMENT(identifier) // 主键自增4. 错误处理
- WCDB 操作返回
BOOL值表示成功/失败 - 可以通过
WCTError获取详细的错误信息
WCTError *error = nil;
BOOL result = [self.database insertObject:obj
intoTable:@"sampleTable"
error:&error];
if (!result && error) {
NSLog(@"插入失败: %@", error.message);
}参考资料
本文档基于 WCDB 2.1.0 版本编写