SQLite
DBManager.h
#import <Foundation/Foundation.h>
@interface StorageItem : NSObject
@property (nonatomic, strong) NSString *key;
@property (nonatomic, strong) NSData *value;
@property (nullable, nonatomic, strong) NSString *filename;
@property (nonatomic) int size;
@property (nonatomic) int modTime;
@property (nonatomic) int accessTime;
@property (nullable, nonatomic, strong) NSData *extendedData;
@end
@interface DBManager : NSObject
- (instancetype)initWithPath:(NSString *)path;
@end
复制代码
DBManager.c
#import "DBManager.h"
#import <QuartzCore/QuartzCore.h>
#import <sqlite3.h>
static const NSUInteger kMaxErrorRetryCount = 8;
static const NSTimeInterval kMinRetryTimeInterval = 2.0;
static const int kPathLengthMax = PATH_MAX - 64;
static NSString *const kDBFileName = @"manifest.sqlite";
static NSString *const kDBShmFileName = @"manifest.sqlite-shm";
static NSString *const kDBWalFileName = @"manifest.sqlite-wal";
@implementation StorageItem
@end
@implementation DBManager {
NSString *_dbPath;
sqlite3 *_db;
CFMutableDictionaryRef _dbStmtCache;
NSTimeInterval _dbLastOpenErrorTime;
NSUInteger _dbOpenErrorCount;
}
-(instancetype)initWithPath:(NSString *)path {
if (path.length == 0 || path.length > kPathLengthMax) {
NSLog(@"DBManager init error: invalid path:[%@].", path);
return nil;
}
self = [super init];
_dbPath = [path stringByAppendingString:kDBFileName];
if (![self _dbOpen] || ![self _dbInitialize]) {
[self _dbClose];
if (![self _dbOpen] || ![self _dbInitialize]) {
[self _dbClose];
NSLog(@"DBManager init error: fail to open sqlite db.");
return nil;
}
}
return self;
}
#pragma mark - db
- (BOOL)_dbOpen {
if (_db) return YES;
int result = sqlite3_open(_dbPath.UTF8String, &_db);
if (result == SQLITE_OK) {
CFDictionaryKeyCallBacks keyCallBacks = kCFTypeDictionaryKeyCallBacks;
CFDictionaryValueCallBacks valueCallBacks = {0};
_dbStmtCache = CFDictionaryCreateMutable(CFAllocatorGetDefault(), 0, &keyCallBacks, &valueCallBacks);
_dbLastOpenErrorTime = 0;
_dbOpenErrorCount = 0;
return YES;
} else {
_db = NULL;
if (_dbStmtCache) CFRelease(_dbStmtCache);
_dbStmtCache = NULL;
_dbLastOpenErrorTime = CACurrentMediaTime();
_dbOpenErrorCount++;
NSLog(@"%s line:%d sqlite open failed (%d).", __FUNCTION__, __LINE__, result);
return NO;
}
}
- (BOOL)_dbClose {
if (_db) return YES;
int result = 0;
BOOL retry = NO;
BOOL stmtFinalized = NO;
if (_dbStmtCache) CFRelease(_dbStmtCache);
_dbStmtCache = NULL;
do {
retry = NO;
result = sqlite3_close(_db);
if (result == SQLITE_BUSY || result == SQLITE_LOCKED) {
if (!stmtFinalized) {
stmtFinalized = YES;
sqlite3_stmt *stmt;
while ((stmt = sqlite3_next_stmt(_db, nil)) != 0) {
sqlite3_finalize(stmt);
retry = YES;
}
}
} else if (result != SQLITE_OK) {
NSLog(@"%s line:%d sqlite close failed (%d).", __FUNCTION__, __LINE__, result);
}
} while (retry);
_db = NULL;
return YES;
}
- (BOOL)_dbCheck {
if (!_db) {
if (_dbOpenErrorCount < kMaxErrorRetryCount && CACurrentMediaTime() - _dbLastOpenErrorTime > kMinRetryTimeInterval) {
return [self _dbOpen] && [self _dbInitialize];
} else {
return NO;
}
}
return YES;
}
- (BOOL)_dbInitialize {
NSString *sql = @"pragma journal_mode = wal; pragma synchronous = normal; create table if not exists mainfest (key text, filename text, size integer, inline_data blob, modification_time integer, last_access_time integer, extended_data blob, primary key(key)); create index if not exists last_access_time_idx on mainfest(last_access_time);";
return [self _dbExecute:sql];
}
- (BOOL)_dbExecute:(NSString *)sql {
if (sql.length == 0) return NO;
if (![self _dbCheck]) return NO;
char *error = NULL;
int result = sqlite3_exec(_db, sql.UTF8String, NULL, NULL, &error);
if (error) {
NSLog(@"%s line:%d sqlite exec error (%d): %s", __FUNCTION__, __LINE__, result, error);
sqlite3_free(error);
}
return result == SQLITE_OK;
}
- (sqlite3_stmt *)_dbPrepareStmt:(NSString *)sql {
if (![self _dbCheck] || sql.length == 0 || !_dbStmtCache) return NULL;
sqlite3_stmt *stmt = (sqlite3_stmt *)CFDictionaryGetValue(_dbStmtCache, (__bridge const void *)(sql));
if (!stmt) {
int result = sqlite3_prepare_v2(_db, sql.UTF8String, -1, &stmt, NULL);
if (result != SQLITE_OK) {
NSLog(@"%s line:%d sqlite stmt prepare error (%d): %s", __FUNCTION__, __LINE__, result, sqlite3_errmsg(_db));
return NULL;
}
CFDictionarySetValue(_dbStmtCache, (__bridge const void *)(sql), stmt);
} else {
sqlite3_reset(stmt);
}
return stmt;
}
- (NSString *)_dbJoinedKeys:(NSArray *)keys {
NSMutableString *string = [NSMutableString new];
for (NSUInteger i = 0, max = keys.count; i < max; i++) {
[string appendString:@"?"];
if (i + 1 != max) {
[string appendString:@","];
}
}
return string;
}
- (void)_dbBindJoinedKeys:(NSArray *)keys stmt:(sqlite3_stmt *)stmt fromIndex:(int)index {
for (int i = 0, max = (int)keys.count; i < max; i++) {
NSString *key = keys[i];
sqlite3_bind_text(stmt, index + i, key.UTF8String, -1, NULL);
}
}
- (BOOL)_dbSaveWithKey:(NSString *)key value:(NSData *)value fileName:(NSString *)fileName extendedData:(NSData *)extendedData {
NSString *sql = @"insert or replace into mainfest (key, filename, size, inline_data, modification_time, last_access_time, extended_data) values (?1, ?2, ?3, ?4, ?5, ?6. ?7);";
sqlite3_stmt *stmt = [self _dbPrepareStmt:sql];
if (!stmt) return NO;
int timestamp = (int)time(NULL);
sqlite3_bind_text(stmt, 1, key.UTF8String, -1, NULL);
sqlite3_bind_text(stmt, 2, fileName.UTF8String, -1, NULL);
sqlite3_bind_int(stmt, 3, (int)value.length);
if (fileName.length == 0) {
sqlite3_bind_blob(stmt, 4, value.bytes, (int)value.length, 0);
} else {
sqlite3_bind_blob(stmt, 4, NULL, 0, 0);
}
sqlite3_bind_int(stmt, 5, timestamp);
sqlite3_bind_int(stmt, 6, timestamp);
sqlite3_bind_blob(stmt, 7, extendedData.bytes, (int)extendedData.length, 0);
int result = sqlite3_step(stmt);
if (result != SQLITE_DONE) {
NSLog(@"%s line: %d sqlite insert error (%d): %s", __FUNCTION__, __LINE__, result, sqlite3_errmsg(_db));
return NO;
}
return YES;
}
- (BOOL)_dbUpdateAccessTimeWithKey:(NSString *)key {
NSString *sql = @"update mainfest set last_access_time = ?1 where key = ?2;";
sqlite3_stmt *stmt = [self _dbPrepareStmt:sql];
if (!stmt) return NO;
sqlite3_bind_int(stmt, 1, (int)time(NULL));
sqlite3_bind_text(stmt, 2, key.UTF8String, -1, NULL);
sqlite3_bind_text(stmt, 2, key.UTF8String, -1, NULL);
int result = sqlite3_step(stmt);
if (result != SQLITE_DONE) {
NSLog(@"%s line: %d sqlite update error (%d): %s", __FUNCTION__, __LINE__, result, sqlite3_errmsg(_db));
return NO;
}
return YES;
}
- (BOOL)_dbUpdateAccessTimeWithKeys:(NSArray *)keys {
if (![self _dbCheck]) return NO;
int t = (int)time(NULL);
NSString *sql = [NSString stringWithFormat:@"update mainfest set last_access_time = %d where key in (%@);", t, [self _dbJoinedKeys:keys]];
sqlite3_stmt *stmt = NULL;
int result = sqlite3_prepare_v2(_db, sql.UTF8String, -1, &stmt, NULL);
if (result != SQLITE_OK) {
NSLog(@"%s line: %d sqlite stmt prepare error (%d): %s", __FUNCTION__, __LINE__, result, sqlite3_errmsg(_db));
return NO;
}
[self _dbBindJoinedKeys:keys stmt:stmt fromIndex:1];
result = sqlite3_step(stmt);
sqlite3_finalize(stmt);
if (result != SQLITE_DONE) {
NSLog(@"%s line: %d sqlite stmt prepare error (%d): %s", __FUNCTION__, __LINE__, result, sqlite3_errmsg(_db));
return NO;
}
return YES;
}
- (BOOL)_dbDeleteItemWithKey:(NSString *)key {
NSString *sql = @"delete from mainfest where key = ?1;";
sqlite3_stmt *stmt = [self _dbPrepareStmt:sql];
if (!stmt) return NO;
sqlite3_bind_text(stmt, 1, key.UTF8String, -1, NULL);
int result = sqlite3_step(stmt);
if (result != SQLITE_DONE) {
NSLog(@"%s line: %d sqlite stmt prepare error (%d): %s", __FUNCTION__, __LINE__, result, sqlite3_errmsg(_db));
return NO;
}
return YES;
}
- (BOOL)_dbDeleteItemWithKeys:(NSArray *)keys {
if (![self _dbCheck]) return NO;
NSString *sql = [NSString stringWithFormat:@"delete from mainfest where key in (%@);", [self _dbJoinedKeys:keys]];
sqlite3_stmt *stmt = NULL;
int result = sqlite3_prepare_v2(_db, sql.UTF8String, -1, &stmt, NULL);
if (result != SQLITE_OK) {
NSLog(@"%s line: %d sqlite stmt prepare error (%d): %s", __FUNCTION__, __LINE__, result, sqlite3_errmsg(_db));
return NO;
}
[self _dbBindJoinedKeys:keys stmt:stmt fromIndex:1];
result = sqlite3_step(stmt);
sqlite3_finalize(stmt);
if (result == SQLITE_ERROR) {
NSLog(@"%s line: %d sqlite stmt prepare error (%d): %s", __FUNCTION__, __LINE__, result, sqlite3_errmsg(_db));
return NO;
}
return YES;
}
- (BOOL)_dbDeleteItemsWithSizeLargerThan:(int)size {
NSString *sql = @"delete from mainfest where size > ?1;";
sqlite3_stmt *stmt = [self _dbPrepareStmt:sql];
if (!stmt) return NO;
sqlite3_bind_int(stmt, 1, size);
int result = sqlite3_step(stmt);
if (result != SQLITE_DONE) {
NSLog(@"%s line: %d sqlite stmt prepare error (%d): %s", __FUNCTION__, __LINE__, result, sqlite3_errmsg(_db));
return NO;
}
return YES;
}
- (BOOL)_dbDeleteItemsWithTimerEarlierThan:(int)time {
NSString *sql = @"delete from mainfest where last_access_time < ?1;";
sqlite3_stmt *stmt = [self _dbPrepareStmt:sql];
if (!stmt) return NO;
sqlite3_bind_int(stmt, 1, time);
int result = sqlite3_step(stmt);
if (result != SQLITE_DONE) {
NSLog(@"%s line: %d sqlite stmt prepare error (%d): %s", __FUNCTION__, __LINE__, result, sqlite3_errmsg(_db));
return NO;
}
return YES;
}
- (StorageItem *)_dbGetItemFromStmt:(sqlite3_stmt *)stmt exculdeInlineData:(BOOL)exculdeInlineData {
int i = 0;
char *key = (char *)sqlite3_column_text(stmt, i++);
char *fileName = (char *)sqlite3_column_text(stmt, i++);
int size = sqlite3_column_int(stmt, i++);
const void *inline_data = exculdeInlineData ? NULL : sqlite3_column_blob(stmt, i);
int inline_data_bytes = exculdeInlineData ? 0 : sqlite3_column_bytes(stmt, i++);
int modification_time = sqlite3_column_int(stmt, i++);
int last_access_time = sqlite3_column_int(stmt, i++);
const void *extended_data = sqlite3_column_blob(stmt, i);
int extended_data_bytes = sqlite3_column_bytes(stmt, i++);
StorageItem *item = [StorageItem new];
if (key) item.key = [NSString stringWithUTF8String:key];
if (fileName && *fileName != 0) item.filename = [NSString stringWithUTF8String:fileName];
item.size = size;
if (inline_data_bytes > 0 && inline_data) item.value = [NSData dataWithBytes:inline_data length:inline_data_bytes];
item.modTime = modification_time;
item.accessTime = last_access_time;
if (extended_data_bytes > 0 && extended_data) item.extendedData = [NSData dataWithBytes:extended_data length:extended_data_bytes];
return item;
}
- (StorageItem *)_dbGetItemWithKey:(NSString *)key exculdeInlineData:(BOOL)exculdeInlineData {
NSString *sql = exculdeInlineData ? @"select key, filename, size, modification_time, last_access_time, extended_data from mainfest where key = ?1;" : @"select key, filename, size, inline_data, modification_time, last_access_time, extended_data from mainfest where key = ?1;";
sqlite3_stmt *stmt = [self _dbPrepareStmt:sql];
if (!stmt) return nil;
sqlite3_bind_text(stmt, 1, key.UTF8String, -1, NULL);
StorageItem *item = nil;
int result = sqlite3_step(stmt);
if (result == SQLITE_ROW) {
item = [self _dbGetItemFromStmt:stmt exculdeInlineData:exculdeInlineData];
} else {
if (result != SQLITE_DONE) {
NSLog(@"%s line: %d sqlite stmt prepare error (%d): %s", __FUNCTION__, __LINE__, result, sqlite3_errmsg(_db));
}
}
return item;
}
- (NSData *)_dbGetValueWithKey:(NSString *)key {
NSString *sql = @"select inline_data from mainfest where key = ?1;";
sqlite3_stmt *stmt = [self _dbPrepareStmt:sql];
if (!stmt) return nil;
sqlite3_bind_text(stmt, 1, key.UTF8String, -1, NULL);
int result = sqlite3_step(stmt);
if (result == SQLITE_ROW) {
const void *inline_data = sqlite3_column_blob(stmt, 0);
int inline_data_bytes = sqlite3_column_bytes(stmt, 0);
if (!inline_data || inline_data_bytes <= 0) return nil;
return [NSData dataWithBytes:inline_data length:inline_data_bytes];
} else {
if (result != SQLITE_DONE) {
NSLog(@"%s line: %d sqlite stmt prepare error (%d): %s", __FUNCTION__, __LINE__, result, sqlite3_errmsg(_db));
}
return nil;
}
}
- (NSString *)_dbGetFileNameWithKey:(NSString *)key {
NSString *sql = @"select filename from mainfest where key = ?1;";
sqlite3_stmt *stmt = [self _dbPrepareStmt:sql];
if (!stmt) return nil;
sqlite3_bind_text(stmt, 1, key.UTF8String, -1, NULL);
int result = sqlite3_step(stmt);
if (result == SQLITE_ROW) {
char *filename = (char *)sqlite3_column_text(stmt, 0);
if (filename && *filename != 0) {
return [NSString stringWithUTF8String:filename];
} else {
if (result != SQLITE_DONE) {
NSLog(@"%s line: %d sqlite stmt prepare error (%d): %s", __FUNCTION__, __LINE__, result, sqlite3_errmsg(_db));
}
}
}
return nil;
}
- (NSMutableArray *)_dbGetFilenameWithKeys:(NSArray *)keys {
if (![self _dbCheck]) return nil;
NSString *sql = [NSString stringWithFormat:@"select filename from mainfest where key in (%@);", [self _dbJoinedKeys:keys]];
sqlite3_stmt *stmt = NULL;
int result = sqlite3_prepare_v2(_db, sql.UTF8String, -1, &stmt, NULL);
if (result != SQLITE_OK) {
NSLog(@"%s line:%d sqlite stmt prepare error (%d): %s", __FUNCTION__, __LINE__, result, sqlite3_errmsg(_db));
return nil;
}
[self _dbBindJoinedKeys:keys stmt:stmt fromIndex:1];
NSMutableArray *filenames = [NSMutableArray new];
do {
result = sqlite3_step(stmt);
if (result == SQLITE_ROW) {
char *filename = (char *)sqlite3_column_text(stmt, 0);
if (filename && *filename != 0) {
NSString *name = [NSString stringWithUTF8String:filename];
if (name) [filenames addObject:name];
}
} else if (result == SQLITE_DONE) {
break;
} else {
NSLog(@"%s line:%d sqlite query error (%d): %s", __FUNCTION__, __LINE__, result, sqlite3_errmsg(_db));
filenames = nil;
break;
}
} while (1);
sqlite3_finalize(stmt);
return filenames;
}
- (NSMutableArray *)_dbGetItemSizeInfoOrderByTimeAscWithLimit:(int)count {
NSString *sql = @"select key, filename, size from mainfest order by last_access_time asc limit ?1;";
sqlite3_stmt *stmt = [self _dbPrepareStmt:sql];
if (!stmt) return nil;
sqlite3_bind_int(stmt, 1, count);
NSMutableArray *items = [NSMutableArray new];
do {
int result = sqlite3_step(stmt);
if (result == SQLITE_ROW) {
char *key = (char *)sqlite3_column_text(stmt, 0);
char *filename = (char *)sqlite3_column_text(stmt, 1);
int size = sqlite3_column_int(stmt, 2);
NSString *keyStr = key ? [NSString stringWithUTF8String:key] : nil;
if (keyStr) {
StorageItem *item = [StorageItem new];
item.key = keyStr;
item.filename = filename ? [NSString stringWithUTF8String:filename] : nil;
item.size = size;
[items addObject:item];
}
} else if (result == SQLITE_DONE) {
break;
} else {
NSLog(@"%s line:%d sqlite query error (%d): %s", __FUNCTION__, __LINE__, result, sqlite3_errmsg(_db));
items = nil;
break;
}
} while (1);
return items;
}
@end
复制代码
上述代码列举了sqlite的使用。sqlite在使用时格式固定。下面我们来分析一下:
- 导入头文件
#import <sqlite3.h>
复制代码
- 声明数据库连接句柄
// Database Connection Handle
sqlite3 *_db;
复制代码
- 打开数据库
int sqlite3_open(
const char *filename, /* Database filename (UTF-8) */
sqlite3 **ppDb /* OUT: SQLite db handle */
);
复制代码
- 创建表命令
NSString *sql = @"pragma journal_mode = wal; pragma synchronous = normal; create table if not exists mainfest (key text, filename text, size integer, inline_data blob, modification_time integer, last_access_time integer, extended_data blob, primary key(key)); create index if not exists last_access_time_idx on mainfest(last_access_time);";
复制代码
- 执行SQL
int sqlite3_exec(
sqlite3*, /* An open database */
const char *sql, /* SQL to be evaluated */
int (*callback)(void*,int,char**,char**), /* Callback function */
void *, /* 1st argument to callback */
char **errmsg /* Error msg written here */
);
复制代码
- 预编译
int sqlite3_prepare_v2(
sqlite3 *db, /* Database handle */
const char *zSql, /* SQL statement, UTF-8 encoded */
int nByte, /* Maximum length of zSql in bytes. */
sqlite3_stmt **ppStmt, /* OUT: Statement handle */
const char **pzTail /* OUT: Pointer to unused portion of zSql */
);
复制代码
- 数据绑定
int sqlite3_bind_null(sqlite3_stmt*, int);
int sqlite3_bind_int(sqlite3_stmt*, int, int);
int sqlite3_bind_double(sqlite3_stmt*, int, double);
int sqlite3_bind_text(sqlite3_stmt*,int,const char*,int,void(*)(void*));
int sqlite3_bind_blob(sqlite3_stmt*, int, const void*, int n, void(*)(void*));
复制代码
- 分步执行
// Evaluate An SQL Statement
int sqlite3_step(sqlite3_stmt*);
复制代码
- 取出数据
int sqlite3_column_int(sqlite3_stmt*, int iCol);
double sqlite3_column_double(sqlite3_stmt*, int iCol);
const unsigned char *sqlite3_column_text(sqlite3_stmt*, int iCol);
int sqlite3_column_bytes(sqlite3_stmt*, int iCol);
const void *sqlite3_column_blob(sqlite3_stmt*, int iCol);
复制代码
- 关闭数据库,置空数据库连接句柄
int sqlite3_close(sqlite3*);
_db = NULL;
复制代码
FMDB
看完上面密密麻麻的代码,估计很多人已经一个头两个大。作为开发者,我们要知道SQLite的使用,但是如果在代码中频繁的编写上述代码,还是很痛苦的。接下来我们来看一下第三方框架:FMDB。
首先还是来看一下FMDB中都有哪些类。
- FMDatabase.h
- FMResultSet.h
- FMDatabaseQueue.h
- FMDatabaseAdditions.h
- FMDatabasePool.h
我们来实际使用一下FMDB
#import <Foundation/Foundation.h>
#import <sqlite3.h>
@class HotMovieModel;
@interface FMDBShare : NSObject
+ (instancetype)shareDataBase;
- (NSString *)filePathWithFileName:(NSString *)fileName;
- (BOOL)createTable;
- (BOOL)insertInfoIntoTable:(HotMovieModel *)model;
- (NSMutableArray *)selectFromTableWithId:(NSInteger)Id;
- (NSMutableArray *)selectAllInfoFromTable;
@end
复制代码
#import "FMDBShare.h"
#import "FMDatabase.h"
#import "HotMovieModel.h"
#define HotMovie @"hotMovie.db"
@interface FMDBShare ()
@property (nonatomic, strong) FMDatabase *dbBase;
@end
@implementation FMDBShare
+ (instancetype)shareDataBase {
static dispatch_once_t once;
static FMDBShare *share;
dispatch_once(&once, ^{
share = [[FMDBShare alloc] init];
if (![share.dbBase open]) {
NSLog(@"%@", [share.dbBase lastErrorMessage]);
return;
}
[share createTable];
});
return share;
}
- (NSString *)filePathWithFileName:(NSString *)fileName {
NSString *fileDirectory = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES)[0];
return [fileDirectory stringByAppendingPathComponent:fileName];
}
- (FMDatabase *)dbBase {
if (_dbBase) {
return _dbBase;
}
_dbBase = [FMDatabase databaseWithPath:[self filePathWithFileName:HotMovie]];
return _dbBase;
}
- (BOOL)createTable {
BOOL result = [self.dbBase executeUpdate:@"create table if not exists HotMovie(Id integer primary key, img text, nm text, mk float, showInfo text, scm text)"];
if (!result) {
NSLog(@"%@", [self.dbBase lastErrorMessage]);
}
return result;
}
- (BOOL)insertInfoIntoTable:(HotMovieModel *)model {
BOOL result = [self.dbBase executeUpdate:@"insert into HotMovie(Id, img, nm, mk, showInfo, scm) values (?,?,?,?,?,?)", @(model.Id), model.img, model.nm, @(model.mk), model.showInfo, model.scm];
if (!result) {
NSLog(@"%@", [self.dbBase lastErrorMessage]);
}
return result;
}
- (NSMutableArray *)selectFromTableWithId:(NSInteger)Id {
FMResultSet *set = [self.dbBase executeQuery:@"select * from HotMovie where Id=?", @(Id)];
while ([set next]) {
return [NSMutableArray arrayWithObject:[HotMovieModel modelWithDictionary:[set resultDictionary]]];
}
return nil;
}
- (NSMutableArray *)selectAllInfoFromTable {
FMResultSet *set = [self.dbBase executeQuery:@"select * from HotMovie"];
NSMutableArray *mArr = [NSMutableArray array];
while ([set next]) {
HotMovieModel *model = [HotMovieModel modelWithDictionary:[set resultDictionary]];
[mArr addObject:model];
}
return mArr;
}
@end
复制代码
使用FMDB框架,我们就可以从繁琐的SQLite中解脱出来,专注于查询逻辑的设计。FMDB也帮我们集成了数据库的回滚操作。
CoreData
CoreData是苹果提供的数据库存储方案,以面向对象的方式去操作数据库。
下面我们使用CoreData创建两张表:
// 获取Context
AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
NSPersistentContainer *persistentContainer = appDelegate.persistentContainer;
NSManagedObjectContext *context = persistentContainer.viewContext;
// 新增数据
TB_Example *exampleModel = [NSEntityDescription insertNewObjectForEntityForName:@"TB_Example" inManagedObjectContext:context];
exampleModel.typeInt = 1;
exampleModel.typeString = @"String";
exampleModel.typeBool = YES;
exampleModel.typeDate = [NSDate date];
exampleModel.typeFloat = 1.0;
exampleModel.typeDouble = 1.0;
NSError *error = nil;
[context save:&error];
// 查询数据
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:@"TB_Example" inManagedObjectContext:context];
[fetchRequest setEntity:entity];
// Specify criteria for filtering which objects to fetch
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"typeString=String"];
[fetchRequest setPredicate:predicate];
// Specify how the fetched objects should be sorted
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"typeInt" ascending:YES];
[fetchRequest setSortDescriptors:[NSArray arrayWithObjects:sortDescriptor, nil]];
NSArray *fetchedObjects = [context executeFetchRequest:fetchRequest error:&error];
if (fetchedObjects == nil) {
// Error handling code
}
// 修改数据
exampleModel.typeInt = 2;
exampleModel.typeString = @"newString";
[context save:&error];
// 删除数据
[context deleteObject:exampleModel];
[context save:&error];
复制代码
参考源码:YYKit