IOS 數據持久化

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

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,106評論 6 542
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,441評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,211評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,736評論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,475評論 6 412
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,834評論 1 328
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,829評論 3 446
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,009評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,559評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,306評論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,516評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,038評論 5 363
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,728評論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,132評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,443評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,249評論 3 399
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,484評論 2 379

推薦閱讀更多精彩內容