IOS 數據持久化的各種方式
1.plist文件存儲
?每個iOS應用都有自己的應用沙盒(應用沙盒就是文件系統目錄),與其他文件系統隔離。應用必須待在自己的沙盒里,其他應用不能訪問該沙盒
應用沙盒的文件系統目錄,如下圖所示(假設應用的名稱叫Layer)
模擬器應用沙盒的根路徑在: (apple是用戶名, 6.0是模擬器版本)
/Users/apple/Library/Application Support/iPhone Simulator/6.0/Applications
Document?保存應用運行時生成的需要持久化的數據,iTunes同步設備時會備份該目錄。例如,游戲應用可將游戲存檔保存在該目錄
temp?保存應用運行時所需的臨時數據,使用完畢后再將相應的文件從該目錄刪除。應用沒有運行時,系統也可能會清除該目錄下的文件。iTunes同步設備時不會備份該目錄
Library/Caches?保存應用運行時生成的需要持久化的數據,iTunes同步設備時不會備份該目錄。一般存儲體積大、不需要備份的非重要數據
Library/Preference:?保存應用的所有偏好設置,iOS的Settings(設置)應用會在該目錄中查找應用的設置信息。iTunes同步設備時會備份該目錄
下面是示例代碼:
1/** 保存數據*/ 2- (IBAction)saveBtn 3{ 4NSLog(@"保存"); 5//1.獲取沙盒根路徑 6NSString *homePath = NSHomeDirectory(); 7//2.document路徑 8NSString *path = [homePath stringByAppendingPathComponent:@"Documents"]; 9//3.新建數據10NSArray *array = [NSArray arrayWithObjects:@"nan",@(22), nil];11NSDictionary *dict = @{@"sss":@"sddd",@"ssssaw":@(1222)};121314//4.存儲數據15NSString *fullPath1 = [path stringByAppendingPathComponent:@"data1.plist"];16NSString *fullPath2 = [path stringByAppendingPathComponent:@"data2.plist"];1718[array writeToFile:fullPath1 atomically:YES];//數組寫入plist19? ? [dict writeToFile:fullPath2 atomically:YES];2021NSLog(@"dict - %@",dict);22}2324/** 讀取數據*/25- (IBAction)readBtn26{27//1.獲取home目錄28NSString *home = NSHomeDirectory();29//2.拼接Document目錄30NSString *path = [home stringByAppendingPathComponent:@"Documents"];31//3.文件路徑32NSString *filePath1 = [path stringByAppendingPathComponent:@"data1.plist"];33NSString *filePath2 = [path stringByAppendingPathComponent:@"data2.plist"];34//讀取文件35NSArray *array = [NSArray arrayWithContentsOfFile:filePath1];36NSDictionary *dict = [NSDictionary dictionaryWithContentsOfFile:filePath2];37NSLog(@"array - %@, dict - %@",array , dict);38}
缺點:只能存儲含有?writeToFile:方法的對象,如NSDictionary,NSArray等.
2.偏好設置 -- 存放目錄?Library/Preference:
很多iOS應用都支持偏好設置,比如保存用戶名、密碼、字體大小等設置,iOS提供了一套標準的解決方案來為應用加入偏好設置功能
每個應用都有個NSUserDefaults實例,通過它來存取偏好設置
比如,保存用戶名、字體大小、是否自動登錄
下面是示例代碼
/** 保存數據*/- (IBAction)save
{
? ? // 1.利用NSUserDefaults,就能直接訪問軟件的偏好設置(Library/Preferences)是個單例對象NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
? ? //2.存儲數據[defaults setObject:@"sd"forKey:@"user"];//其中要保存的數據是setObject:@"sd",@“sd”是數據,forKey:@"user"是以user的鍵值保存的,
/**
key - value
@"user" -@"sd"
*/[defaults setObject:@"123w"forKey:@"test"];
? ? [defaults setInteger:20forKey:@"age"];
? ? [defaults setBool:YES forKey:@"auto_login"];
? ? //3.立刻同步(相當于更新數據)? ? [defaults synchronize];
}/** 讀取數據*/- (IBAction)read
{
? ? //1.建立NSUserDefaults對象NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
? ? //2.讀取數據NSString *user = [defaults objectForKey:@"user"];
? ? NSString *test = [defaults objectForKey:@"test"];
? ? NSInteger age = [defaults integerForKey:@"age"];
? ? BOOL autoLogin = [defaults boolForKey:@"auto_login"];
? ? NSLog(@"user - %@\n test - %@\n age - %d\n autoLogin - %d\n,",user,test,age,autoLogin);}
//下面是解檔歸檔基本OC對象
- (void)test
{
??NSString *mName = @"zhangsan";
// 獲取doc的目錄
NSString *docPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
? ? // 拼接保存的路徑
? ? NSString *filePath = [docPath stringByAppendingPathComponent:@"dataList"];
//? ? NSData *mData = [mName dataUsingEncoding:NSUTF8StringEncoding];
? ? [NSKeyedArchiver archiveRootObject:mName toFile:filePath];
? ? NSString *testName = [NSKeyedUnarchiver unarchiveObjectWithFile:filePath];
? ? NSLog(@"解檔出來的數據是 ----- %@",testName);
}
注意:UserDefaults設置數據時,不是立即寫入,而是根據時間戳定時地把緩存中的數據寫入本地磁盤。所以調用了set方法之后數據有可能還沒有寫入磁盤應用程序就終止了。出現以上問題,可以通過調用synchornize方法強制寫入[defaults synchornize];
缺點:本質還是plist文件存儲,相對于plist文件存儲來講存儲數據更快捷.
3.NSKeyedArchiver(NSCoding)
如果對象是NSString、NSDictionary、NSArray、NSData、NSNumber等類型,可以直接用NSKeyedArchiver進行歸檔和恢復
不是所有的對象都可以直接用這種方法進行歸檔,只有遵守了NSCoding協議的對象才可以
NSCoding協議有2個方法:
encodeWithCoder:
每次歸檔對象時,都會調用這個方法。一般在這個方法里面指定如何歸檔對象中的每個實例變量,可以使用encodeObject:forKey:方法歸檔實例變量
initWithCoder:
每次從文件中恢復(解碼)對象時,都會調用這個方法。一般在這個方法里面指定如何解碼文件中的數據為對象的實例變量,可以使用decodeObject:forKey方法解碼實例變量
示例代碼
1#import 2 3@interfacePerson : NSObject 4 5@property (nonatomic , copy) NSString *name; 6@property (nonatomic , assign)int age; 7@property (nonatomic , assign)double height; 8 9@end101112#import"Person.h"1314@implementation Person1516//歸檔的時候調用17/** 將某個對象寫入文件的時候會調用,在這個方法中說明哪些對象的哪些屬性需要存儲*/18- (void)encodeWithCoder:(NSCoder *)enCoder19{20NSLog(@"enCoder - %@",enCoder);21[enCoder encodeObject:self.name forKey:@"name"];22[enCoder encodeInt:self.age forKey:@"age"];23[enCoder encodeDouble:self.height forKey:@"height"];2425}2627/** 解檔時候調用,在這個方法中說清楚哪些屬性要解檔*/28- (id)initWithCoder:(NSCoder *)decoder29{30if(self = [super init])31? ? {32//讀取文件內容33self.name = [decoder decodeObjectForKey:@"name"];34self.age = [decoder decodeIntForKey:@"age"];35self.height = [decoder decodeDoubleForKey:@"height"];3637? ? }3839return self;40}414243@end444546#import"SDViewController.h"47#import"Person.h"4849@interface SDViewController ()5051@end5253@implementation SDViewController5455- (void)viewDidLoad56{57? ? [super viewDidLoad];585960}6162- (IBAction)save63{64//1.數據對象65Person *p1 = [[Person alloc] init];66p1.name =@"王麻子";67p1.age =20;68p1.height =1.98;69Person *p2 = [[Person alloc] init];70p2.name =@"李四";71p2.age =56;72p2.height =1.68;73//2.歸檔數據對象74//2.1獲得文件的Documents全路徑75NSString *doc = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)lastObject];76//2.2獲得文件的全路徑77NSString *path = [doc stringByAppendingPathComponent:@"person.txt"];78//2.3將對象歸檔79? ? [NSKeyedArchiver archiveRootObject:p1 toFile:path];8081}82- (IBAction)read83{84// 1.獲得Documents的全路徑85NSString *doc = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];86// 2.獲得文件的全路徑87NSString *path = [doc stringByAppendingPathComponent:@"person.txt"];88// 3.從文件中讀取Person對象89Person *p3 = [NSKeyedUnarchiver unarchiveObjectWithFile:path];9091NSLog(@"%@ %d %f", p3.name, p3.age, p3.height);9293}949596@end
NSKeyedArchiver-歸檔對象的注意:
如果父類也遵守了NSCoding協議,請注意:應該在encodeWithCoder:方法中加上一句[super encodeWithCode:encode];確保繼承的實例變量也能被編碼,
即也能被歸檔應該在initWithCoder:方法中加上一句self = [super initWithCoder:decoder];確保繼承的實例變量也能被解碼,即也能被恢復
但有時候可能想將多個對象寫入到同一個文件中,那么就要使用NSData來進行歸檔對象NSData可以為一些數據提供臨時存儲空間,以便隨后寫入文件,或者存放從磁盤讀取的文件內容。
示例代碼
? 1Person.h里面? 2? 3#import? 4? 5@interfacePerson : NSObject ? 6? 7@property (nonatomic , copy) NSString *name;? 8@property (nonatomic , assign)int age;? 9@property (nonatomic , assign)double height; 10 11@end 12 13Person.m里面 14#import"Person.h" 15 16@implementation Person 17 18//歸檔的時候調用 19/** 將某個對象寫入文件的時候會調用,在這個方法中說明哪些對象的哪些屬性需要存儲*/ 20- (void)encodeWithCoder:(NSCoder *)enCoder 21{ 22NSLog(@"enCoder - %@",enCoder); 23[enCoder encodeObject:self.name forKey:@"name"]; 24[enCoder encodeInt:self.age forKey:@"age"]; 25[enCoder encodeDouble:self.height forKey:@"height"]; 26 27} 28 29/** 解檔時候調用,在這個方法中說清楚哪些屬性要解檔*/ 30- (id)initWithCoder:(NSCoder *)decoder 31{ 32if(self = [super init]) 33? ? { 34//讀取文件內容 35self.name = [decoder decodeObjectForKey:@"name"]; 36self.age = [decoder decodeIntForKey:@"age"]; 37self.height = [decoder decodeDoubleForKey:@"height"]; 38 39? ? } 40 41return self; 42} 43 44 45@end 46 47SDViewController.m里面 48 49#import"SDViewController.h" 50#import"Person.h" 51 52@interface SDViewController () 53 54@end 55 56@implementation SDViewController 57 58- (void)viewDidLoad 59{ 60? ? [super viewDidLoad]; 61// Do any additional setup after loading the view, typically from a nib. 62 63} 64 65- (void)didReceiveMemoryWarning 66{ 67? ? [super didReceiveMemoryWarning]; 68// Dispose of any resources that can be recreated. 69} 70 71 72- (IBAction)save 73{ 74//1.數據對象 75Person *p1 = [[Person alloc] init]; 76p1.name =@"王麻子"; 77p1.age =20; 78p1.height =1.98; 79Person *p2 = [[Person alloc] init]; 80p2.name =@"李四"; 81p2.age =56; 82p2.height =1.68; 83//2.歸檔數據對象 84//2.1獲得文件的Documents全路徑 85NSString *doc = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)lastObject]; 86//2.2獲得文件的全路徑 87NSString *path = [doc stringByAppendingPathComponent:@"person.txt"]; 88////2.3將對象歸檔 89//? ? [NSKeyedArchiver archiveRootObject:p1 toFile:path]; 90// 91// 新建一塊可變數據區 92NSMutableData *data = [NSMutableData data]; 93// 將數據區連接到一個NSKeyedArchiver對象 94NSKeyedArchiver *archiver = [[NSKeyedArchiver alloc] initForWritingWithMutableData:data]; 95[archiver encodeObject:p1 forKey:@"person1"]; 96[archiver encodeObject:p2 forKey:@"person2"]; 97// 存檔完畢(一定要調用這個方法) 98? ? [archiver finishEncoding]; 99//將存檔的數據寫入文件100? ? [data writeToFile:path atomically:YES];101102}103- (IBAction)read104{105// 1.獲得Documents的全路徑106NSString *doc = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];107// 2.獲得文件的全路徑108NSString *path = [doc stringByAppendingPathComponent:@"person.txt"];109// 3.從文件中讀取Student對象110//? ? Person *p3 = [NSKeyedUnarchiver unarchiveObjectWithFile:path];111NSData *data = [NSData dataWithContentsOfFile:path];112113// 根據數據,解析成一個NSKeyedUnarchiver對象114NSKeyedUnarchiver *unchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:data];115Person *p1 = [unchiver decodeObjectForKey:@"person1"];116Person *p2 = [unchiver decodeObjectForKey:@"person2"];117? ? [unchiver finishDecoding];118NSLog(@"%@ %d %f", p1.name, p1.age, p1.height);119NSLog(@"%@ %d %f", p2.name, p2.age, p2.height);120121}122123124125@end
NSCoder.h
- (void)encodeValueOfObjCType:(const?char *)type at:(const?void *)addr;?//解檔C類型數據,addr傳入地址
- (void)encodeDataObject:(NSData *)data;?//歸檔一個NSData型對象
- (void)decodeValueOfObjCType:(const?char *)type at:(void *)data;?////解檔C類型數據
- (NSData *)decodeDataObject;//解檔一個NSData型對象
@end
@interface NSCoder (NSExtendedCoder)
/**?歸檔相關函數*/
- (void)encodeObject:(id)object;
- (void)encodeRootObject:(id)rootObject;
- (void)encodeBycopyObject:(id)anObject;
- (void)encodeByrefObject:(id)anObject;
- (void)encodeConditionalObject:(id)object;
- (void)encodeValuesOfObjCTypes:(const?char *)types, ...;
- (void)encodeArrayOfObjCType:(const?char *)type count:(NSUInteger)count at:(const?void *)array;
- (void)encodeBytes:(const?void *)byteaddr length:(NSUInteger)length;
/**?解檔相關函數*/
- (id)decodeObject;
- (void)decodeValuesOfObjCTypes:(const?char *)types, ...;
- (void)decodeArrayOfObjCType:(const?char *)itemType count:(NSUInteger)count at:(void *)array;
- (void *)decodeBytesWithReturnedLength:(NSUInteger *)lengthp NS_RETURNS_INNER_POINTER;
- (unsigned)systemVersion;
//是否遵循KeyedCoding
- (BOOL)allowsKeyedCoding;
/**?歸檔相關函數*/
- (void)encodeObject:(id)objv forKey:(NSString *)key;
- (void)encodeConditionalObject:(id)objv forKey:(NSString *)key;
- (void)encodeBool:(BOOL)boolv forKey:(NSString *)key;
- (void)encodeInt:(int)intv forKey:(NSString *)key;
- (void)encodeInt32:(int32_t)intv forKey:(NSString *)key;
- (void)encodeInt64:(int64_t)intv forKey:(NSString *)key;
- (void)encodeFloat:(float)realv forKey:(NSString *)key;
- (void)encodeDouble:(double)realv forKey:(NSString *)key;
- (void)encodeBytes:(const uint8_t *)bytesp length:(NSUInteger)lenv forKey:(NSString *)key;
/**?解檔相關函數*/
- (BOOL)containsValueForKey:(NSString *)key;
- (id)decodeObjectForKey:(NSString *)key;
- (BOOL)decodeBoolForKey:(NSString *)key;
- (int)decodeIntForKey:(NSString *)key;
- (int32_t)decodeInt32ForKey:(NSString *)key;
- (int64_t)decodeInt64ForKey:(NSString *)key;
- (float)decodeFloatForKey:(NSString *)key;
- (double)decodeDoubleForKey:(NSString *)key;
- (const uint8_t *)decodeBytesForKey:(NSString *)key returnedLength:(NSUInteger *)lengthp NS_RETURNS_INNER_POINTER;?// returned bytes immutable!
- (void)encodeInteger:(NSInteger)intv forKey:(NSString *)key NS_AVAILABLE(10_5,?2_0);
- (NSInteger)decodeIntegerForKey:(NSString *)key NS_AVAILABLE(10_5,?2_0);
// Returns YES if this coder requires secure coding. Secure coders check a list of allowed classes before decoding objects, and all objects must implement NSSecureCoding.
//是否安全解檔
- (BOOL)requiresSecureCoding NS_AVAILABLE(10_8,?6_0);
SQLite3
SQLite3是一款開源的嵌入式關系型數據庫,可移植性好、易使用、內存開銷小.SQLite3是無類型的,意味著你可以保存任何類型的數據到任意表的任意字段中。
?數據庫語句
/*簡單約束*/
?//如果表不存在就創建一張t_student的表(id為主鍵,自動增長,name text類型,age integer類型);
CREATE TABLE IF NOT EXISTS t_student(id?INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT, age INTEGER);
?//如果表不存在就創建一張t_student的表(id為主鍵,自動增長,name text類型不能為空,age integer類型不能為空);
CREATE TABLE IF NOT EXISTS t_student(id?INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT?NULL, age INTEGER NOT?NULL);
?//如果表不存在就創建一張t_student的表(id為主鍵,自動增長,name text類型,并且每一個是唯一的,age integer類型不能為空);
CREATE TABLE IF NOT EXISTS t_student(id?INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT UNIQUE, age INTEGER);
//如果表不存在就創建一張t_student的表(id為主鍵,自動增長,name text類型,age integer類型默認為1);
CREATE TABLE IF NOT EXISTS t_student(id?INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT, age INTEGER DEFAULT 1);
/*分頁*/
//先將表按照升序排,跳過最前面30條語句,然后取10條記錄
SELECT * FROM t_student ORDER BY?id?ASC LIMIT 30, 10;
/*排序*/
//取出表中score > 50的數據并按照降序排列
SELECT * FROM t_student WHERE score > 50 ORDER BY age DESC;
//取出表中score<50的數據并按照升序排列,score>50的數據按照降序排列
SELECT * FROM t_student WHERE score < 50 ORDER BY age ASC , score DESC;
/*計量*/
//統計表中age > 50的個數
SELECT COUNT(*) FROM t_student WHERE age > 50;
/*別名*/
//將name 命名為 myName, age 命名為 myAge, score 命名為myScore
SELECT name as myName, age as myAge, score as myScore FROM t_student;
//將name 命名為 myName, age 命名為 myAge, score 命名為myScore
SELECT name myName, age myAge, score myScore FROM t_student;
//給t_student表起個別名叫做s,利用s來引用表中的字段,取出age > 50 的數據,將name 命名為 myName, age 命名為 myAge, score 命名為myScore
SELECT s.name myName, s.age myAge, s.score myScore FROM t_student s WHERE s.age > 50;
/*查詢*/
//從表中查詢name,age,score
SELECT name, age, score FROM t_student;
//查詢整張表
SELECT * FROM t_student;
/*修改指定數據*/
//從表中取出age = 10 的那條數據,將name 字段值設為MM
UPDATE t_student SET name =?'MM'?WHERE age = 10;
/從表中取出age = 7 的那條數據,將name 字段值設為WW
UPDATE t_student SET name =?'WW'?WHERE age is 7;
//取出表中age < 20 的數據,并將name 全部設置為XXOO
UPDATE t_student SET name =?'XXOO'?WHERE age < 20;
//取出表中age < 50 并且 score > 10的數據,將滿足條件的每一條數據中的name字段設置為NNMM
UPDATE t_student SET name =?'NNMM'?WHERE age < 50 and score > 10;
/*刪除數據*/
//刪除表中的數據
DELETE FROM t_student;
/*更新數據*/
//將表中的name字段全部設置為MM
UPDATE t_student SET name =?'MM';
/*插入數據*/
//往表中插入一條數據(name = jonathan , age = 28, score = 100)
?INSERT INTO t_student(age, score, name) VALUES ('28', 100,?'jonathan');
//往表中插入一條數據(name = lee , age = 28)
?INSERT INTO t_student(name, age) VALUES ('lee',?'28');
//往表中插入一條數據(score =? 100)
?INSERT INTO t_student(score) VALUES (100);
/*添加主鍵*/
//如果表不存在就創建一張表,id為主鍵自動增長,integer類型,name 為text類型,age 為integer類型,score為浮點類型
CREATE TABLE IF NOT EXISTS t_student (id?INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT, age INTEGER, score REAL);
/*刪除表*/
//銷毀t_student表
DROP TABLE t_student;
//如果表存在就銷毀這張表
DROP TABLE IF EXISTS t_student;
/******************************************************? 應 用? *****************************************************************/
- (void)viewDidLoad
{
????[super?viewDidLoad];
????// 1.打開創建一個數據庫
????NSString?*path = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,?NSUserDomainMask,?YES) lastObject];
????NSString?*fileName = [path stringByAppendingPathComponent:@"t_student.sqlite"];
????int?result = sqlite3_open(fileName.UTF8String, &_db);
????if?(result == SQLITE_OK) {
????????// 創建表
????????const?char?*sql =?"CREATE TABLE IF NOT EXISTS t_student(id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL , name TEXT NOT NULL, age INTERGER NOT NULL);";
????????char?*error =?nil;
????????sqlite3_exec(self.db, sql,?NULL,?NULL, &error);
????????if?(error) {
????????????NSLog(@"創建失敗");
????????}else{
????????????NSLog(@"創建成功");
????????}
????}else{
????????NSLog(@"失敗");
????}
}
/**
?*? 增加
?*/
- (IBAction)insertBtnClick:(id)sender {
????for?(int?i = 0; i < 100; i++) {
????????NSString?*name = [NSString?stringWithFormat:@"lee-%d", i];
????????int?age = arc4random_uniform(50) + 50;
????????NSString?*sql = [NSString?stringWithFormat:@"INSERT INTO t_student(name, age) VALUES ('%@', %d);", name, age];
????????char?*error =?nil;
????????sqlite3_exec(self.db, sql.UTF8String,?NULL,?NULL, &error);
????????if?(error) {
????????????NSLog(@"創建失敗");
????????}else{
????????????NSLog(@"創建成功");
????????}
????}
}
/**
?*? 更新
?*/
- (IBAction)updateBtnClick:(id)sender {
????const?char?*sql =?"UPDATE t_student SET name = 'DG';";
????char?*error =?nil;
????sqlite3_exec(self.db, sql,?NULL,?NULL, &error);
????if?(error) {
????????NSLog(@"更新失敗");
????}else{
????????NSLog(@"更新成功");
????}
}
/**
?*? 刪除
?*/
- (IBAction)deleteBtnClick:(id)sender {
????const?char?*sql =?"DELETE FROM t_student;";
????char?*error =?nil;
????sqlite3_exec(self.db, sql,?NULL,?NULL, &error);
????if?(error) {
????????NSLog(@"刪除失敗");
????}else{
????????NSLog(@"刪除成功");
????}
}
/**
?*? 查詢
?*/
- (IBAction)selectBtnClick:(id)sender {
????const?char?*zSql =?"SELECT * FROM t_student";
????sqlite3_stmt *stmt;
????// 查詢前的準備, 檢查sql語句是否正確
????int?result = sqlite3_prepare_v2(self.db, zSql, -1, &stmt,?NULL);
????if(result == SQLITE_OK) {?// 準備完成,沒有錯誤
????????// 提取查詢到得數據到stmt, 一次提取一條
????????while(sqlite3_step(stmt) == SQLITE_ROW){
????????????// 取出提取到得記錄(數據)中的第0列數據和第一列數據
????????????const?unsigned?char?*name = sqlite3_column_text(stmt, 0);
????????????int?age = sqlite3_column_int(stmt, 1);
????????????NSLog(@"%s, %d", name, age);
????????}
????}
}
SqLite3小結
1.打開數據庫
int?sqlite3_open(
????const?char?*filename,?// 數據庫的文件路徑
????sqlite3 **.ppDb?// 數據庫實例
);
2.執行任何SQL語句
int?sqlite3_exec(
????sqlite3*,?// 一個打開的數據庫實例
????const?char?*sql,?// 需要執行的SQL語句
????int?(*callback)(void*,int,char**,char**),?// SQL語句執行完畢后的回調
????void?*,?// 回調函數的第1個參數
????char?**errmsg?// 錯誤信息
);
3.檢查SQL語句的合法性(查詢前的準備)
int?sqlite3_prepare_v2(
????sqlite3 *db,?// 數據庫實例
????const?char?*zSql,?// 需要檢查的SQL語句
????int?nByte,?// SQL語句的最大字節長度
????sqlite3_stmt **ppStmt,?// sqlite3_stmt實例,用來獲得數據庫數據
????const?char?**pzTail
);
4.查詢一行數據
int?sqlite3_step(sqlite3_stmt*);?// 如果查詢到一行數據,就會返回SQLITE_ROW
5.利用stmt獲得某一字段的值(字段的下標從0開始)
double?sqlite3_column_double(sqlite3_stmt*,?int?iCol);?// 浮點數據
int?sqlite3_column_int(sqlite3_stmt*,?int?iCol);?// 整型數據
sqlite3_int64 sqlite3_column_int64(sqlite3_stmt*,?int?iCol);?// 長整型數據
const?void?*sqlite3_column_blob(sqlite3_stmt*,?int?iCol);?// 二進制文本數據
const?unsigned?char?*sqlite3_column_text(sqlite3_stmt*,?int?iCol);?// 字符串數據
6.SqLite3第三方框架FMDB使用小結
?FMDB是iOS平臺的SQLite數據庫框架,FMDB以OC的方式封裝了SQLite的C語言API.
FMDB的優點
@1使用起來更加面向對象,省去了很多麻煩、冗余的C語言代碼
@2對比蘋果自帶的Core Data框架,更加輕量級和靈活
@3提供了多線程安全的數據庫操作方法,有效地防止數據混亂
?FMDB簡單使用示例代碼
#import"CZViewController.h"#import"FMDB.h"@interface CZViewController ()- (IBAction)insertBtnClick;- (IBAction)updateBtnClick;- (IBAction)deleteBtnClick;- (IBAction)queryBtnClick;
@property (nonatomic, strong) FMDatabase *db;@end@implementation CZViewController- (void)viewDidLoad
{
? ? [super viewDidLoad];
? ? // 0.獲取沙盒路徑NSString *path = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)lastObject];
? ? NSString *fileName = [path stringByAppendingPathComponent:@"t_student.sqlite"];
? ? // 1.獲得數據庫對象self.db = [FMDatabase databaseWithPath:fileName];
? ? // 2.打開數據庫if ([self.db open]) {
? ? ? ? NSLog(@"打開成功");
? ? ? ? // 2.1創建表BOOL success =? [self.db executeUpdate:@"CREATE TABLE IF NOT EXISTS t_student (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, name TEXT NOT NULL, age INTEGER NOT NULL);"];
? ? ? ? if (success) {
? ? ? ? ? ? NSLog(@"創建表成功");
? ? ? ? }else? ? ? ? {
? ? ? ? ? ? NSLog(@"創建表失敗");
? ? ? ? }
? ? }else? ? {
? ? ? ? NSLog(@"打開失敗");
? ? }
}- (IBAction)insertBtnClick
{
? ? for(inti =0; i <100; i++) {
? ? ? ? NSString *name = [NSString stringWithFormat:@"Jonathan-%d", i];
? ? ? ? intage = arc4random_uniform(1000);
? ? ? ? BOOL success = [self.db executeUpdate:@"INSERT INTO t_student(name , age) VALUES(?, ?);", name, @(age)];// 注意只能拼接對象類型if (success) {
? ? ? ? ? ? NSLog(@"添加成功");
? ? ? ? }else? ? ? ? {
? ? ? ? ? ? NSLog(@"添加失敗");
? ? ? ? }
? ? }
}- (IBAction)updateBtnClick
{
? ? BOOL success = [self.db executeUpdate:@"UPDATE t_student SET name = 'JACK' WHERE age < 50"];
? ? if (success) {
? ? ? ? NSLog(@"修改成功");
? ? }else? ? {
? ? ? ? NSLog(@"修改失敗");
? ? }
}- (IBAction)deleteBtnClick
{
? ? BOOL success = [self.db executeUpdate:@"DELETE FROM t_student WHERE age < ?;", @50];
? ? if (success) {
? ? ? ? NSLog(@"刪除成功");
? ? }else? ? {
? ? ? ? NSLog(@"刪除失敗");
? ? }
}- (IBAction)queryBtnClick
{
? ? // 1.查詢//? ? FMResultSet *set = [self.db? executeQuery:@"SELECT id, name, age FROM t_student;"];FMResultSet *set= [self.db? executeQuery:@"SELECT * FROM t_student;"];
? ? // 2.取出數據while([set next]) {
? ? ? ? // 取出姓名//? ? ? NSString *name = [set stringForColumnIndex:1];
? ? ? ? // 取出年齡//? ? ? int age = [set intForColumnIndex:2];NSString *name = [setstringForColumn:@"name"];
? ? ? ? intage = [setintForColumn:@"age"];
? ? ? ? NSLog(@"name = %@, age = %d", name, age);
? ? }
}@end
FMDB線程安全
#import"CZViewController.h"#import"FMDB.h"@interface CZViewController ()- (IBAction)insertBtnClick;- (IBAction)updateBtnClick;- (IBAction)deleteBtnClick;- (IBAction)queryBtnClick;
@property (nonatomic, strong) FMDatabase *db;
@property (nonatomic, strong) FMDatabaseQueue *queue;@end@implementation CZViewController- (void)viewDidLoad
{
? ? [super viewDidLoad];
? ? // 0.獲取沙盒路徑NSString *path = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)lastObject];
? ? NSString *fileName = [path stringByAppendingPathComponent:@"t_student.sqlite"];
? ? // 1.獲得數據庫對象//? ? self.db = [FMDatabase databaseWithPath:fileName];
? ? // 1.活的數據庫隊列對象self.queue = [FMDatabaseQueue databaseQueueWithPath:fileName];
? ? // 2.在數據庫隊列中執行線程安全的操作[self.queue inDatabase:^(FMDatabase *db) {
? ? ? ? if ([db open]) {
? ? ? ? ? ? // 2.1創建表BOOL success =? [db executeUpdate:@"CREATE TABLE IF NOT EXISTS t_person (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, name TEXT NOT NULL, money INTEGER NOT NULL);"];
? ? ? ? ? ? if (success) {
? ? ? ? ? ? ? ? NSLog(@"創建表成功");
? ? ? ? ? ? }else? ? ? ? ? ? {
? ? ? ? ? ? ? ? NSLog(@"創建表失敗");
? ? ? ? ? ? }
? ? ? ? }
? ? }];
}- (IBAction)insertBtnClick
{
? ? [self.queue inDatabase:^(FMDatabase *db) {
? ? ? ? [db executeUpdate:@"INSERT INTO t_person(name, money) VALUES('Zs', 1000)"];
? ? ? ? [db executeUpdate:@"INSERT INTO t_person(name, money) VALUES('LS', 1000)"];
? ? }];
}- (IBAction)updateBtnClick
{
? ? /*? ? [self.queue inDatabase:^(FMDatabase *db) {
? ? ? ? [db beginTransaction]; // 開啟事務
? ? ? ? [db executeUpdate:@"update t_person set money = 0 where name = 'Zs';"];
//? ? ? ? [db rollback];// 主動回滾
? ? ? ? [db executeUpdate:@"update t_person set money = 2000 where name = 'LS';"];
? ? ? ? [db commit];// 提交事務
? ? }];
? ? */? ?
? ? [self.queue inTransaction:^(FMDatabase *db, BOOL *rollback) {
? ? ? ? [db executeUpdate:@"update t_person set money = 0 where name = 'Zs';"];
? ? ? ? NSLog(@"主動回滾");
? ? ? ? *rollback = YES;
? ? ? ? [db executeUpdate:@"update t_person set money = 2000 where name = 'LS';"];
? ? }];
}- (IBAction)deleteBtnClick
{
? [self.queue inDatabase:^(FMDatabase *db) {
? ? ? BOOL success = [db executeUpdate:@"DELETE FROM t_student WHERE age < ?;", @50];
? ? ? if (success) {
? ? ? ? ? NSLog(@"刪除成功");
? ? ? }else? ? ? {
? ? ? ? ? NSLog(@"刪除失敗");
? ? ? }
? }];
}- (IBAction)queryBtnClick
{
? ? [self.queue inDatabase:^(FMDatabase *db) {
? ? ? ? // 1.查詢FMResultSet *set= [db? executeQuery:@"SELECT * FROM t_student;"];
? ? ? ? // 2.取出數據while([set next]) {
? ? ? ? ? ? NSString *name = [setstringForColumn:@"name"];
? ? ? ? ? ? intage = [setintForColumn:@"age"];
? ? ? ? ? ? NSLog(@"name = %@, age = %d", name, age);
? ? ? ? }
? ? }];
}@end