数据存储

分析数据存储方式,我们首先从底层的实现去分析,然后往上看。首先,我们来看一下如何使用SQLite去操作数据库。

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在使用时格式固定。下面我们来分析一下:

  1. 导入头文件
#import <sqlite3.h>
复制代码
  1. 声明数据库连接句柄
// Database Connection Handle
sqlite3 *_db;
复制代码
  1. 打开数据库
int sqlite3_open(
  const char *filename,   /* Database filename (UTF-8) */
  sqlite3 **ppDb          /* OUT: SQLite db handle */
);
复制代码
  1. 创建表命令
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);";
复制代码
  1. 执行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 */
);
复制代码
  1. 预编译
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 */
);
复制代码
  1. 数据绑定
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*));
复制代码
  1. 分步执行
// Evaluate An SQL Statement
int sqlite3_step(sqlite3_stmt*);
复制代码
  1. 取出数据
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);
复制代码
  1. 关闭数据库,置空数据库连接句柄
int sqlite3_close(sqlite3*);
_db = NULL;
复制代码
FMDB

看完上面密密麻麻的代码,估计很多人已经一个头两个大。作为开发者,我们要知道SQLite的使用,但是如果在代码中频繁的编写上述代码,还是很痛苦的。接下来我们来看一下第三方框架:FMDB。

首先还是来看一下FMDB中都有哪些类。

  1. FMDatabase.h
  2. FMResultSet.h
  3. FMDatabaseQueue.h
  4. FMDatabaseAdditions.h
  5. 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整体面貌,SQLite中的每张table表对应的是NSManagedObject,NSManagedObject存在于NSManagedObjectContext。我们通过context去执行数据的增删改查等。NSManagedObjectContext受NSPersistentStoreCoordinator管理。NSPersistentStoreCoordinator包含有NSPersistentStore,NSPersistentStore对应一个SQLite文件,SQLite文件存储于FileSystem下。

下面我们使用CoreData创建两张表:

我们可以切换表的显示形式,更直观的感受两者的关联。
执行下图所示操作,然后按照提示执行,Xcode会生成相应的文件

// 获取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

转载于:https://juejin.im/post/5adfca756fb9a07a9d6ffcea

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值