SQLite
DBManager.h
#import@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#import 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复制代码
- 声明数据库连接句柄
// Database Connection Handlesqlite3 *_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 Statementint 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#import @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];复制代码
参考源码: