原文連接html
有開發經驗的同窗應該都清楚,不論是前端,客戶端有些數據都須要在本地落地的;那麼這一章咱們就來一塊兒瞭解一下 iOS 的數據存取經常使用的方法。前端
在對 iOS 數據 進行具體的操做以前,咱們須要首先了解一下 Bundle 和沙盒的概念。java
Bundle 和沙盒是 iOS 數據存儲的具體地方;linux
Bundle 是一個目錄,其中包含了程序會使用到的資源.這些資源包含了如圖像,聲音,編譯好的代碼,nib 文件等待android
使用 pathForResource 的路徑爲真實環境 APP 下面的路徑ios
// 加載Info.plist資源,mainBundle的資源都是放到RootFolder下
NSString *infoPath= [[NSBundle mainBundle] pathForResource:@"Info" ofType:@"plist"];
NSLog(@"Info路徑爲: %@",infoPath);
// 找到圖片路徑
NSString *imgPath = [[NSBundle mainBundle] pathForResource:@"ff_IconAdd_25x25_@2x" ofType:@"png"];
NSLog(@"圖片路徑爲: %@",imgPath);
UIImageView *img = [[UIImageView alloc] initWithFrame:CGRectMake(100, 100, 100, 100)];
img.image = [UIImage imageWithContentsOfFile:imgPath];
[self.view addSubview:img];
複製代碼
輸出爲:git
Info路徑爲: /Users/xxx/Library/Developer/CoreSimulator/Devices/8EA1B565-17F4-47FF-8ECA-C23F3EF4594A/data/Containers/Bundle/Application/76750996-9428-448E-8662-553CA22DCB28/NSBundle的使用.app/Info.plist
圖片路徑爲: /Users/xxxx/Library/Developer/CoreSimulator/Devices/8EA1B565-17F4-47FF-8ECA-C23F3EF4594A/data/Containers/Bundle/Application/76750996-9428-448E-8662-553CA22DCB28/NSBundle的使用.app/ff_IconAdd_25x25_@2x.png
複製代碼
其餘經常使用的方法github
//獲取XML文件
NSString *filePath = [[NSBundle mainBundle] pathForResouse:@"re" ofType:@"xml"];
NSData *data = [[NSData alloc] initWithContentsOfFile:filePath];
//獲取app包的readme.txt文件路徑
NSString *path = [[NSBundle mainBundle] pathForResource:@"readme" ofType:@"txt"];
//app資源目錄路徑
NSString *resPath = [[NSBundle mainBundle] resourcePath];
//獲取app包路徑
NSString *path = [[NSBundle mainBundle] bundlePath];
//經過使用下面的方法獲得程序的main bundle
NSBundle *otherBundle = [NSBundle mainBundle];
//獲取資源目錄下a.bundle
NSString *path = [resPath stringByAppendingPathComponent:@"a.bundle"];
NSBundle *bundle = [NSBundle bundleWithPath:path];
//一旦咱們有了bundle,就能夠訪問其中的資源文件了。
NSString path = [otherBundle pathForImageResource:@"img"];
NSImage *img = [[NSImage alloc] initWithContentsOfFile:path];
//bundle中能夠包含一個庫. 若是咱們從庫獲得一個class, bundle會鏈接庫,並查找該類:
Class newClass = [otherBundle classNamed:@"Person"];
id person = [[newClass alloc] init];
//若是不知到class名,也能夠經過查找主要類來取得
Class aClass = [otherBundle principalClass];
id classInstance = [[aClass alloc] init];
//能夠看到, NSBundle有不少的用途.在這章中, NSBundle負責(在後臺)加載nib文件. 咱們也能夠不經過NSWindowController來加載nib文件, 直接使用NSBundle:
BOOL flag = [NSBundle loadNibNamed:@"ViewController" owner:someObject];
//注意: 咱們指定了一個對象someObject做爲nib的File」s Owner
//獲取屬性列表
NSDictionary *dict = [NSDictionary dictionaryWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"ViewControllers" ofType:@"plist"]]
————————————————
複製代碼
獲取 mainBundle 包內容信息web
/1.獲取app的info.plist詳細信息
//build 版本號
NSString *version = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleVersion"];
//version 版本號
NSString *version2 = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleShortVersionString"];
//應用標識:Bundle identifier
NSString *bundleId = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleIdentifier"];
//Bundle name
NSString *appName = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleName"];
//應用展現的名字
NSString *appName = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleDisplayName"];
//2.應用程序語言本地化
//app本地化宏
#define XLocalizedString(key, comment)[[NSBundle mainBundle] localizedStringForKey:(key) value:@"" table:nil]
複製代碼
每個 IOS 程序都是單獨運行在一個沙盒內部的,以保證不會篡改或者讀取其餘 APP 的信息。sql
其中 Data Container 包含以下三個目錄:
Documents
: 應用程序在運行時生成的一些須要長久保存的重要數據放在此文件中;
Library
:
Cache
:存放緩存文件,好比從網路上下載的數據或數據。通常用來保存應用須要長期使用的,數據量大,不須要備份的非重要數據。iTunesPrefrence
:保存應用的全部偏好設置,好比帳號,設置等。由系統自動管理tmp
: 用於保存應用在運行時產生的一些臨時數據文件,手機重啓,系統空間不足的,關閉應用等場景下可能會刪除該文件下的文件
沙盒入口
NSLog(@"%@",NSHomeDirectory());
沙盒 tmp 目錄
NSLog(@"%@",NSTemporaryDirectory());
沙盒 Documents 目錄
// 1: 拼接方式
NSLog(@"%@",[NSHomeDirectory() stringByAppendingPathComponent:@"Documents"]);
// 沙盒Documents目錄 2: 建議使用的方式
NSLog(@"%@",[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject]);
複製代碼
// 1: 拼接方式
NSLog(@"%@",[NSHomeDirectory() stringByAppendingPathComponent:@"Library"]);
// 沙盒Library目錄 2: 建議使用的方式
NSLog(@"%@",[NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES) firstObject]);
複製代碼
NSLog(@"%@",[NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) firstObject]);
複製代碼
NSLog(@"%@",[[NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES) firstObject] stringByAppendingPathComponent:@"Preferences"]);
plist 文件本質上是一個 xml 文件,按照key:value
的形式存儲。
讀取系統自帶的 Info.plist
NSDictionary *sysInfo = NSBundle.mainBundle.infoDictionary;
NSLog(@"%@",sysInfo);
NSLog(@"%@",sysInfo[@"CFBundleShortVersionString"]);
複製代碼
自定義的 plist 文件
// 可使用NSBundle.mainBundle pathForResource 讀取項目工程目錄下的文件
NSString *userPath = [NSBundle.mainBundle pathForResource:@"user" ofType:@"plist"];
NSDictionary *userDic = [NSDictionary dictionaryWithContentsOfFile:userPath];
NSLog(@"username= %@ , password = %@",userDic[@"username"],userDic[@"password"]);
// 解析一個複雜的plist; value爲array的狀況
NSString *cityPath = [NSBundle.mainBundle pathForResource:@"cityData" ofType:@"plist"];
NSDictionary *cityDic = [NSDictionary dictionaryWithContentsOfFile:cityPath];
NSArray *allKeys = cityDic.allKeys;
for (int i=0; i<allKeys.count; i++) {
NSLog(@" key = %@ , value = %@",allKeys[i],cityDic[allKeys[i]]);
}
// 讀取沙盒中Documents中的數據
NSString *path = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask, YES)[0];
NSString *dataPath = [path stringByAppendingPathComponent:@"data.plist"];
NSDictionary *dataDict = [NSDictionary dictionaryWithContentsOfFile:path];
NSLog(@"%@", dataDict);
複製代碼
注意點
dictionaryWithContentsOfFile
方法// 將cityData.plist存儲到沙盒的Documents中
NSString *saveFilePath = [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject] stringByAppendingPathComponent:@"city.plist"];
[cityDic writeToFile:saveFilePath atomically:YES];
// 寫入部分數據
NSString *fujianPath = [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject] stringByAppendingPathComponent:@"福建.plist"];
[cityDic[@"福建"] writeToFile:fujianPath atomically:YES];
複製代碼
NSUserDefaults 偏好設置存儲,不須要路徑,通常用於存儲帳號密碼等信息。
// 存數據
- (void)saveForPreference {
// 獲取偏好設置對象
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
//存儲數據
[defaults setObject:@"mrgao" forKey:@"name"];
[defaults setInteger:24 forKey:@"age"];
// 同步調用,馬上寫到文件中,不寫這個方法會異步,有延遲
[defaults synchronize];
}
//讀數據
- (void)readForPreference {
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSString *name = [defaults objectForKey:@"name"];
NSInteger *age = [defaults integerForKey:@"age"];
NSLog(@"%@-------%ld",name,age);
}
複製代碼
舉一個保存用戶名和密碼的例子:
//
// ViewController.m
// NSBundle的使用
#import "ViewController.h"
@interface ViewController ()
@property (weak, nonatomic) IBOutlet UITextField *userName;
@property (weak, nonatomic) IBOutlet UITextField *password;
- (IBAction)savePassword:(id)sender;
- (IBAction)login:(id)sender;
@property (weak, nonatomic) IBOutlet UISwitch *remenber;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
NSDictionary *info = [self readForPreference];
self.userName.text =info[@"name"];
self.password.text =info[@"pass"];
// 字典類型,傳遞Bool類型的值,須要轉換成 NSNumber; 獲取值的時候,先要獲取NSNumber,而後在獲取對應的boolean的值
self.remenber.on =[info[@"isOn"] boolValue];
}
// 存數據
- (void)saveForPreference {
// 獲取偏好設置對象
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSString *username = self.userName.text;
NSString *password = self.password.text;
//存儲數據
[defaults setObject:username forKey:@"username"];
[defaults setObject:password forKey:@"password"];
// [defaults setInteger:24 forKey:@"age"];
// 同步調用,馬上寫到文件中,不寫這個方法會異步,有延遲
[defaults synchronize];
}
//讀數據
- (NSDictionary *)readForPreference {
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSString *name = [defaults valueForKey:@"username"];
NSString *pass = [defaults valueForKey:@"password"];
Boolean isOn = [defaults boolForKey:@"isOn"];
// NSInteger *age = [defaults integerForKey:@"age"];
NSLog(@"%@-------%@",name,pass);
// 字典類型,傳遞Bool類型的值,須要轉換成 NSNumber
return @{@"name":name,@"pass":pass,@"isOn": [NSNumber numberWithBool:isOn]};
}
- (IBAction)savePassword:(id)sender {
UISwitch *swc = (UISwitch *)sender;
NSLog(@"%@", self.remenber.isOn ? @"YES" : @"NO");
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults setBool:self.remenber.isOn forKey:@"isOn"];
[defaults synchronize];
}
- (IBAction)login:(id)sender {
[self saveForPreference];
}
@end
複製代碼
注意點
NSDictionary 不能直接傳播 Bool 類型的值,須要先將其轉換成 NSNumber 而後讀取的時候轉換成 bool;
NSLog 打印的時候不能直接打印 Bool 類型的值,建議使用這種方式展現: NSLog(@"%@", boolValue ? @"YES" : @"NO");
偏好設置存儲 記得調用 [defaults synchronize];
存儲
那麼咱們使用 NSUserDefaults 保存的數據最終存放到哪裏?
前面咱們講過沙盒裏面有一個 Library->Preferences 是專門用來存放應用的全部偏好設置,好比帳號,設置等,由系統自動管理。
那咱們一探究竟:
結論: NSUserDefaults 的數據最終存放在沙盒下的 Library->Preferences 文件夾中,且類型爲 plist 類型所謂新特性頁面,就是在一個 APP 的某一個版本下,按照的時候展現一次該特性頁面;之後再次打開就不展現了。例如:王者榮耀新賽季更新的時候,開場賽季介紹動畫播放,當此賽季咱們再次打開的時候 就不會播放此動畫了.
- (void)scene:(UIScene *)scene willConnectToSession:(UISceneSession *)session options:(UISceneConnectionOptions *)connectionOptions {
// Override point for customization after application launch.
self.window = [[UIWindow alloc] initWithWindowScene:(UIWindowScene *)scene];
// 獲取當前應用的版本
NSString *currentVersion = NSBundle.mainBundle.infoDictionary[@"CFBundleShortVersionString"];
// 獲取到本地的版本
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSString *localVersion = [defaults valueForKey:@"version"];
// 若是版本更新了就顯示新特性頁面
if (![currentVersion isEqualToString:localVersion]) {
UIViewController *newVC = [[UIViewController alloc] init];
newVC.view.backgroundColor = UIColor.greenColor;
self.window.rootViewController= newVC;
// 保存當前版本
[defaults setObject:currentVersion forKey:@"version"];
[defaults synchronize];
}else{
UIViewController *normalVC = [[UIViewController alloc] init];
normalVC.view.backgroundColor = UIColor.redColor;
self.window.rootViewController= normalVC;
}
[self.window makeKeyAndVisible];
}
複製代碼
歸檔通常都是保存自定義對象的時候,使用歸檔.由於 plist 文件不可以保存自定義對象.若是一個字典當中保存有自定義對象,若是把這個字典寫入到文件當中,它是不會生成 plist 文件的.
NSArray, NSString, NSInteger, NSDictionary 等 iOS 自帶的數據類型,歸檔是原生支持的。
@interface NSKeyedArchiverViewController ()
- (IBAction)archive:(id)sender;
- (IBAction)unarchive:(id)sender;
@property(strong,nonatomic) NSData *data;
@end
@implementation NSKeyedArchiverViewController
- (IBAction)archive:(id)sender {
// 數據存儲,此處案例是序列化一個數組
self.data =[NSKeyedArchiver archivedDataWithRootObject:@[@"a",@"b",@"c"] requiringSecureCoding:YES error:nil];
}
- (IBAction)unarchive:(id)sender {
// 數據讀取,轉換成數組
NSArray *array= [NSKeyedUnarchiver unarchivedObjectOfClass:NSArray.class fromData:self.data error:nil];
NSLog(@"%@",array);
}
@end
複製代碼
上面講到 NSArray
, NSString
, NSInteger
, NSDictionary
等 iOS 自帶的數據類型,歸檔是原生支持的,可是若是是自定義對象,就和上面的方式有所不一樣。
若是咱們按照上面的方式去將自定義對象歸檔:
NSError *error;
self.userData= [NSKeyedArchiver archivedDataWithRootObject:user requiringSecureCoding:YES error:&error];
if(error!=nil){
NSLog(@"%@",error);
}
複製代碼
確定會報錯:由於咱們沒有讓自定義的對象去遵照 安全編碼NSSecureCoding
協議
Domain=NSCocoaErrorDomain Code=4866 "The data couldn’t be written because it isn’t in the correct format." UserInfo={NSUnderlyingError=0x6000016d0210 {Error Domain=NSCocoaErrorDomain Code=4864 "This decoder will only decode classes that adopt NSSecureCoding. Class 'User' does not adopt it." UserInfo={NSDebugDescription=This decoder will only decode classes that adopt NSSecureCoding. Class 'User' does not adopt it.}}}
複製代碼
因此,咱們自定義對象歸檔有以下幾個步驟:
NSSecureCoding
協議;NSSecureCoding
協議中的三個方法;encodeWithCoder
initWithCoder
supportsSecureCoding
以前使用的數組實際上是已經幫咱們實現了encodeWithCoder
和initWithCoder
;那麼咱們如何編寫?
// User.h 實現NSSecureCoding協議
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface User : NSObject<NSSecureCoding>
@property(nonatomic,copy) NSString *userName;
@property(nonatomic,assign) int age;
@end
NS_ASSUME_NONNULL_END
複製代碼
在 User.m 去實現協議的幾個方法
#import "User.h"
@implementation User
// 編碼,將對象寫入流中
- (void)encodeWithCoder:(nonnull NSCoder *)coder {
// 要記得將全部的對象都序列化
[coder encodeObject:self.userName forKey:@"userName"];
[coder encodeInt:self.age forKey:@"age"];
}
// 解碼,將流初始化爲對象
- (nullable instancetype)initWithCoder:(nonnull NSCoder *)coder {
if(self = [super init]){
self.userName = [coder decodeObjectForKey:@"userName"];
self.age = [coder decodeIntForKey:@"age"];
}
return self;
}
// 是否支持安全編碼
+ (BOOL)supportsSecureCoding{
return YES;
}
@end
複製代碼
自定義對象 User 的歸檔和解檔:
- (IBAction)archive:(id)sender {
// 自定義對象須要存儲在沙盒中
User *user = [[User alloc] init];
user.userName=@"mrgaogang";
user.age=123;
NSError *error;
self.userData= [NSKeyedArchiver archivedDataWithRootObject:user requiringSecureCoding:YES error:&error];
if(error!=nil){
NSLog(@"%@",error);
}
// 咱們也能夠將歸檔的數據存放在沙盒中
NSString *filePath = [NSTemporaryDirectory() stringByAppendingPathComponent:@"User.data"];
[self.userData writeToFile:filePath atomically:YES];
}
- (IBAction)unarchive:(id)sender {
// 從沙盒中取出咱們歸檔的數據
NSData *data = [NSData dataWithContentsOfFile:[NSTemporaryDirectory() stringByAppendingPathComponent:@"User.data"]];
User *user= [NSKeyedUnarchiver unarchivedObjectOfClass:User.class fromData:data error:nil];
NSLog(@"userName = %@ , age = %d",user.userName,user.age);
}
複製代碼
# 打印狀況
userName = mrgaogang , age = 123
複製代碼
SQLite,是一款輕型的數據庫,是遵照 ACID 的關係型數據庫管理系統,它包含在一個相對小的 C 庫中. sqlite 是嵌入式的,佔用內存小,且支持 windows,linux,unix 操做系統,因此當前的 android/ios 都是支持的
sqlite 支持的數據類型:
sqlite 經常使用函數:
因爲 sqlite3 並不是 ios 自帶的,因此咱們須要額外的引入:
因爲操做數據庫,對應的映射確定是一個對象,那麼咱們首先將 Model 構造出來:
//
// Person.h
// SQLite數據庫操做
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface Person : NSObject
@property(nonatomic,copy) NSString *name;
@property(nonatomic,copy) NSString *phone;
@property(nonatomic,copy) NSString *address;
- (instancetype) initWithName:(NSString *) name andPhone:(NSString *) phone andAddress:(NSString *) address;
@end
NS_ASSUME_NONNULL_END
複製代碼
並實現對應的構造函數
//
// Person.m
// SQLite數據庫操做
#import "Person.h"
@implementation Person
- (instancetype)initWithName:(NSString *)name andPhone:(NSString *)phone andAddress:(NSString *)address{
if(self = [super init]){
self.name= name;
self.phone=phone;
self.address=address;
}
return self;
}
- (NSString *)description
{
return [NSString stringWithFormat:@"name = %@ , phone = %@, address = %@", self.name,self.phone,self.address];
}
@end
複製代碼
因爲對數據庫的操做是獨立的,咱們不該該將其放在 UI/Controller 中,而應該將數據庫的操做單獨放在一個文件中進行;以便於後續的維護和程序的開發。
此處主要演示對數據庫的六種操做:
//
// DBTools.h
// SQLite數據庫操做
#import <Foundation/Foundation.h>
#import "Person.h"
NS_ASSUME_NONNULL_BEGIN
@interface DBTools : NSObject
// 建立db
- (void) createDB;
// 建立表
- (void) createTable:(NSString *) tableName;
// 插入數據
- (void) insertPerson:(Person *) person;
// 條件刪除
- (void) deletePerson:(NSString *) personName;
// 更新數據
- (void) updatePerson:(Person *) person;
// 查詢全部數據
- (NSArray<Person *> *) queryPerson;
// 經過條件查詢
- (NSArray<Person *> *) queryPersonByName:(NSString *) personName;
// 數據庫的名字
@property(nonnull,copy) NSString *dbName;
@end
NS_ASSUME_NONNULL_END
複製代碼
//
// DBTools.m
// SQLite數據庫操做
#import "DBTools.h"
#import "Person.h"
#import "sqlite3.h"
@interface DBTools(){
sqlite3 *sqlite;
}
@end
@implementation DBTools
// 建立數據庫,存放在沙盒中
- (void)createDB{
// 1. 查找沙盒
NSString *docPath= [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject];
// 2. 拼接數據庫
NSString *dbPath = [docPath stringByAppendingPathComponent: [NSString stringWithFormat:@"%@.db",self.dbName]];
NSLog(@"%@",dbPath);
// 3. 建立數據庫,須要傳遞2個參數,1: 數據庫名稱(c語言的字符串), 2: 數據庫的句柄,後續咱們須要使用句柄進行數據庫的操做
int result = sqlite3_open([dbPath UTF8String], &sqlite);
if(result == SQLITE_OK){
NSLog(@"數據庫建立成功");
}else{
NSLog(@"數據庫建立失敗");
}
}
// 數據庫的關閉
- (void) closeDB{
sqlite3_close(sqlite);
}
// 針對建立表格,增長,刪除均可以統一使用執行 sqlite3_exec
- (void) execSQL:(NSString *) sql{
// 1. 執行以前須要打開數據庫
[self createDB];
// 2. 建立sql語句
NSLog(@"%@",sql);
char *error;
// 3. 執行sql語句
int result= sqlite3_exec(sqlite, [sql UTF8String], NULL, NULL, &error);
if(result == SQLITE_OK){
NSLog(@"sql執行成功");
}else{
NSLog(@"sql執行失敗, %s" ,error);
}
// 4. 必定要記得關閉數據庫
[self closeDB];
}
// 執行建立表格的sql語句
- (void)createTable:(NSString *)tableName{
NSString *sql = [NSString stringWithFormat: @"create table %@(id integer primary key autoincrement, name text , phone text ,address text)",tableName];
[self execSQL: sql];
}
// 執行插入的sql語句
- (void)insertPerson:(Person *)person{
NSString *sql = [NSString stringWithFormat: @"insert into person (name , phone ,address) values ('%@','%@','%@')",person.name,person.phone,person.address];
[self execSQL: sql];
}
// 執行刪除的sql語句
- (void)deletePerson:(NSString *)personName{
NSString *sql = [NSString stringWithFormat: @"delete from person where name='%@'",personName];
[self execSQL: sql];
}
// 執行更新的sql語句
- (void)updatePerson:(Person *)person{
NSString *sql = [NSString stringWithFormat: @"update person set phone='%@' ,address='%@' where name='%@'",person.phone,person.address,person.name];
[self execSQL:sql];
}
// 查詢所有
- (NSArray<Person *> *)queryPerson{
[self createDB];
NSString *sql = [NSString stringWithFormat: @"select name,phone,address from person"];
sqlite3_stmt *stmt;
// 使用sqlite3_prepare_v2查詢數據
int result= sqlite3_prepare_v2(sqlite, [sql UTF8String], -1, &stmt, NULL);
NSMutableArray *array = [NSMutableArray array];
if(result == SQLITE_OK){
// 遍歷結果集
while (sqlite3_step(stmt) == SQLITE_ROW) {
// 參數爲結果集和查詢結果集的第幾列;且返回的結果爲c語言的string
const char *cName= (const char*) sqlite3_column_text(stmt, 0);
const char *cPhone= (const char*) sqlite3_column_text(stmt, 1);
const char *cAddress= (const char*) sqlite3_column_text(stmt, 2);
// 要記得將c語言的string 改爲NSString
Person *person = [[Person alloc] initWithName:[NSString stringWithUTF8String:cName] andPhone:[NSString stringWithUTF8String:cPhone] andAddress:[NSString stringWithUTF8String:cAddress]];
[array addObject:person];
}
}else{
NSLog(@"查詢失敗");
}
[self closeDB];
return array;
}
// 條件查詢
- (NSArray<Person *> *)queryPersonByName:(NSString *)personName{
[self createDB];
NSString *sql = [NSString stringWithFormat: @"select * from person where name = '%@'",personName];
sqlite3_stmt *stmt;
// 使用sqlite3_prepare_v2查詢數據
int result= sqlite3_prepare_v2(sqlite, [sql UTF8String], -1, &stmt, NULL);
NSMutableArray *array = [NSMutableArray array];
if(result == SQLITE_OK){
// 遍歷結果集
while (sqlite3_step(stmt) == SQLITE_ROW) {
// 參數爲結果集和查詢結果集的第幾列;且返回的結果爲c語言的string
const char *cPhone= (const char*) sqlite3_column_text(stmt, 0);
const char *cAddress= (const char*) sqlite3_column_text(stmt, 1);
// 要記得將c語言的string 改爲NSString
Person *person = [[Person alloc] initWithName:personName andPhone:[NSString stringWithUTF8String:cPhone] andAddress:[NSString stringWithUTF8String:cAddress]];
[array addObject:person];
}
}else{
NSLog(@"查詢失敗");
}
[self closeDB];
return array;
}
@end
複製代碼
//
// ViewController.m
// SQLite數據庫操做
#import "ViewController.h"
#import "DBTools.h"
@interface ViewController ()
- (IBAction)createDB:(id)sender;
- (IBAction)createTable:(id)sender;
- (IBAction)insert:(id)sender;
- (IBAction)deleteData:(id)sender;
- (IBAction)updateData:(id)sender;
- (IBAction)queryData:(id)sender;
@property(nonatomic,strong) DBTools *dbTools;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
self.dbTools = [[DBTools alloc] init];
self.dbTools.dbName = @"person";
}
// 建立數據庫
- (IBAction)createDB:(id)sender {
[self.dbTools createDB];
}
// 建立表格
- (IBAction)createTable:(id)sender {
[self.dbTools createTable:@"person"];
}
// 插入數據
- (IBAction)insert:(id)sender {
Person *person = [[Person alloc] initWithName:@"mrgaogang" andPhone:@"12345678900" andAddress:@"中國-廣東深圳"];
[self.dbTools insertPerson:person];
}
// 刪除數據
- (IBAction)deleteData:(id)sender {
[self.dbTools deletePerson: @"mrgaogang"];
}
// 更新數據
- (IBAction)updateData:(id)sender {
NSArray *array = [self.dbTools queryPersonByName:@"mrgaogang"];
if(array.count > 0){
Person *person = [array firstObject];
[self.dbTools updatePerson:person ];
}
}
// 查詢數據
- (IBAction)queryData:(id)sender {
NSArray *array = [self.dbTools queryPersonByName:@"mrgaogang"];
if(array.count > 0){
for (Person *p in array) {
NSLog(@"%@",p);
}
}
}
@end
複製代碼
FMDB 是什麼?
FMDB 的優勢
此處使用 cocospods 的方式安裝,固然你也能夠將源代碼下面的 fmdb 拷貝下來,放在工程內部。若是不知道如何使用 cocospods的同窗能夠參考此文章:cocospods的使用
pod init
複製代碼
而後編輯Podfile
並新增 FMDB
:
# Uncomment the next line to define a global platform for your project
# platform :ios, '9.0'
target 'FMDB的使用' do
# Comment the next line if you're not using Swift and don't want to use dynamic frameworks
use_frameworks!
# Pods for MyApp2
pod 'FMDB'
# pod 'FMDB/FTS' # FMDB with FTS
# pod 'FMDB/standalone' # FMDB with latest SQLite amalgamation source
# pod 'FMDB/standalone/FTS' # FMDB with latest SQLite amalgamation source and FTS
# pod 'FMDB/SQLCipher' # FMDB with SQLCipher
end
複製代碼
pod install
複製代碼
安裝以後打開: FMDB的使用.xcworkspace 記住 是 : FMDB的使用.xcworkspace
而不是 FMDB的使用.xcodeproj
.
若是編譯有以下報錯
解決辦法:刪除Pods.frameworks,剩餘有Pods_.framework
Model和以前的Person幾乎相似。 聲明Model的屬性及構造函數
//
// Student.h
// FMDB的使用
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface Student : NSObject
@property(nonatomic,copy) NSString *name;
@property(nonatomic,assign) int age;
@property (nonatomic,copy) NSString *sex;
- (instancetype)initWithName:(NSString *) name andAge:(int) age andSex:(NSString *) sex;
@end
NS_ASSUME_NONNULL_END
複製代碼
實現Model的構造函數和description方法
//
// Student.h
// FMDB的使用
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface Student : NSObject
@property(nonatomic,copy) NSString *name;
@property(nonatomic,assign) int age;
@property (nonatomic,copy) NSString *sex;
- (instancetype)initWithName:(NSString *) name andAge:(int) age andSex:(NSString *) sex;
@end
NS_ASSUME_NONNULL_END
複製代碼
其實總體的流程和純操做sqlite3是相似的,主要有以下幾個變化:
建立庫成功以後使用FMDatabase
存儲
再也不直接操做sqlite3_exec而是使用executeUpdate
執行增長,刪除和修改;
使用executeUpdate
的時候,再也不使用字符串拼接的方式,而是使用佔位符?
,且executeUpdate
第一個參數爲sql,後面爲可變參數,參數的個數和?
的個數一致;
使用executeUpdate
的時候,注意傳入的都必須是對象,對於int等基礎類型,須要使用@()
包裹傳遞參數。
查詢操做直接使用executeQuery
,且使用更加直觀的FMResultSet
存儲結果集
從結果集中獲取數據也使用更加直觀的:stringForColumn
, intForColumn
等 獲取
#import "DBTools.h"
#import "FMDB.h"
@interface DBTools ()
@property (strong,nonatomic) FMDatabase *fmdb;
@end
@implementation DBTools
- (void) createDB{
// 1. 找到沙盒路徑
NSString *docPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject];
// 2. 找到db文件
NSString *fileName = [docPath stringByAppendingPathComponent: @"student.db"];
self.fmdb = [FMDatabase databaseWithPath:fileName];
BOOL success = [self.fmdb open];
if(success){
NSLog(@"打開數據庫成功");
}else{
NSLog(@"打開數據庫失敗");
}
}
- (void) createTable{
//用以前必定要打開數據庫
[self createDB];
NSString *sql = @"create table t_stu(id integer primary key autoincrement, name text, age integer, sex text)";
BOOL isSuccess = [self.fmdb executeUpdate:sql];
if(isSuccess){
NSLog(@"建立表成功");
}
else{
NSLog(@"建立表失敗");
}
//用完關閉數據庫
[self.fmdb close];
}
- (void) insertStudent:(Student *) student{
[self createDB];
NSString *sql = @"insert into t_stu(name,age,sex) values (?,?,?)";
// 注意FMDB操做數據必須使用對象,age是int類型,不符合,因此須要轉換成對象的方式
BOOL isSuccess = [self.fmdb executeUpdate:sql,student.name,@(student.age),student.sex];
if(isSuccess){
NSLog(@"插入成功");
}
else{
NSLog(@"插入失敗");
}
//用完關閉數據庫
[self.fmdb close];
}
- (void) deleteStudent:(NSString *) stuName{
[self createDB];
NSString *sql = @"delete from t_stu where name=?";
BOOL isSuccess = [self.fmdb executeUpdate:sql,stuName];
if(isSuccess){
NSLog(@"刪除成功");
}
else{
NSLog(@"刪除失敗");
}
//用完關閉數據庫
[self.fmdb close];
}
- (void) updateStudent:(Student *) student{
[self createDB];
NSString *sql = @"update t_stu set age=? , sex=? where name=?";
BOOL isSuccess = [self.fmdb executeUpdate:sql,@(student.age),student.sex,student.name];
if(isSuccess){
NSLog(@"更新成功");
}
else{
NSLog(@"更新失敗");
}
//用完關閉數據庫
[self.fmdb close];
}
- (NSArray<Student *>*) queryStudentByName:(NSString *) stuName{
[self createDB];
NSString *sql = @"select name,age,sex from t_stu where name=?";
FMResultSet *resultSet=[self.fmdb executeQuery:sql,stuName];
NSMutableArray *array = [NSMutableArray array];
while ([resultSet next]) {
NSString *name = [resultSet stringForColumn:@"name"];
int age = [resultSet intForColumn:@"age"];
NSString *sex = [resultSet stringForColumn:@"sex"];
Student *stu = [[Student alloc] initWithName:name andAge:age andSex:sex];
[array addObject:stu];
}
//用完關閉數據庫
[self.fmdb close];
return array;
}
@end
複製代碼
- (void)insertStudents:(NSArray<Student *> *)students{
[self createDB];
// 開啓事務
[self.fmdb beginTransaction];
@try {
for (Student *student in students) {
NSString *sql = @"insert into t_stu(name,age,sex) values (?,?,?)";
// 注意FMDB操做數據必須使用對象,age是int類型,不符合,因此須要轉換成對象的方式
BOOL isSuccess = [self.fmdb executeUpdate:sql,student.name,@(student.age),student.sex];
if(isSuccess){
NSLog(@"插入成功");
}
else{
NSLog(@"插入失敗");
}
}
} @catch (NSException *exception) {
// 回滾數據
[self.fmdb rollback];
} @finally {
// 提交數據
[self.fmdb commit];
}
//用完關閉數據庫
[self.fmdb close];
}
複製代碼
測試:
NSMutableArray *array = [NSMutableArray array];
for (int i=0; i<1000; i++) {
Student *stu = [[Student alloc] initWithName:@"mrgaogang" andAge:i andSex:@"male"];
[array addObject:stu];
}
[self.dbTools insertStudents: array];
複製代碼
參考