iOS開發簡單高效的數據存儲

?

?

在iOS開發過程中,不管是做什么應用,都會碰到數據保存的問題,你是用什么方法來持久保存數據的?這是在幾乎每一次關于iOS技術的交流或討論都會被提到的問題,而且大家對這個問題的熱情持續高漲。本文主要從概念上把“數據存儲”這個問題進行剖析,并且結合各自特點和適用場景進行全面拋析。。

?

NSUserDefaults

?

NSUserDefaults被設計用來存儲設備和應用的配置信息,它通過一個工廠方法返回默認的、也是最常用到的實例對象。這個對象中儲存了系統中用戶的配置信息,開發者可以通過這個實例對象對這些已有的信息進行修改,也可以按照自己的需求創建新的配置項。?

?

NSUserDefaults可以存儲的數據類型包括:NSData、NSString、NSNumber、NSDate、NSArray、NSDictionary。如果要存儲其他類型,則需要轉換為前面的類型,才能用NSUserDefaults存儲。具體實現為:?

?

保存數據:

?

NSUserDefaults *defaults =[NSUserDefaults standardUserDefaults];

NSString *name =@”default string“;

[defaults setObject:firstName forKey:@"name"];

? //獲得UIImage實例

?

UIImage *image=[[UIImage alloc]initWithContentsOfFile:@"photo.jpg"];

?

NSData *imageData = UIImageJPEGRepresentation(image, 100);//UIImage對象轉換成NSData

?

[defaults synchronize]; //用synchronize方法把數據持久化到standardUserDefaults數據庫

?

讀取數據:

?

NSUserDefaults *defaults =[NSUserDefaults standardUserDefaults];

NSString *name = [defaults objectForKey:@"name"]; //根據鍵值取出name

NSData *imageData = [defaults dataForKey:@"image"];

UIImage *Image = [UIImage imageWithData:imageData]; //NSData轉換為UIImage

?

歸檔,解歸檔

?

NSKeyedArchiver:采用歸檔的形式來保存數據,該數據對象需要遵守NSCoding協議,并且該對象對應的類必須提供encodeWithCoder:和initWithCoder:方法。前一個方法告訴系統怎么對對象進行編碼,而后一個方法則是告訴系統怎么對對象進行解碼。例如對Possession對象歸檔保存。?

?

定義Possession:

?

@interface Possession:NSObject<NSCoding>{//遵守NSCoding協議

? ? ? ?NSString *name;//待歸檔類型

}

@implementation Possession

-(void)encodeWithCoder:(NSCoder *)aCoder{

? ? ? ? ? ? [aCoder encodeObject:name forKey:@"name"];

}

-(void)initWithCoder:(NSCoder *)aDecoder{

? ? ? ? ? ? name=[[aDeCoder decodeObjectforKey:@"name"] retain];

}

?

歸檔操作:

?

如果對Possession對象allPossession歸檔保存,只需要NSCoder子類NSKeyedArchiver的方法archiveRootObject:toFile: 即可。

?

NSString *path = [self possessionArchivePath];

[NSKeyedArchiver archiveRootObject:allPossessions toFile: path ]

?

解壓操作:

?

?

同樣調用NSCoder子類NSKeyedArchiver的方法unarchiveRootObject:toFile: 即可 ?

allPossessions = [[NSKeyedUnarchiver unarchiveObjectWithFile:path] retain];

?

缺點:歸檔的形式來保存數據,只能一次性歸檔保存以及一次性解壓。所以只能針對小量數據,而且對數據操作比較笨拙,即如果想改動數據的某一小部分,還是需要解壓整個數據或者歸檔整個數據。

?

SQLite

?

用SQLite存儲查詢需求較多的數據,是我們開發中最常見的一種方式,例如app的界面數據緩存,離線緩存等。?

?

第一步:需要添加SQLite相關的庫以及頭文件:在項目文件的Build Phases下,找到Link Binary Library(ies),添加libsqlite3.0.dylib?

(libsqlite3.dylib與前者的區別暫時不知,兩者應該差不多);在項目文件中頭文件或者源文件中添加頭文件#import “/usr/include/sqlite3.h”

?

第二步:開始使用SQLite:

?

NSArray *documentsPaths=NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask , YES);

NSString *databaseFilePath=[[documentsPaths objectAtIndex:0] stringByAppendingPathComponent:@"mydb"];

//上面兩句已經比較熟悉了吧!?

//打開數據庫

if (sqlite3_open([databaseFilePath UTF8String], &database)==SQLITE_OK) {?

? ? ? ? NSLog(@"sqlite dadabase is opened.");?

}

else{ return;}//打開不成功就返回

在打開了數據庫的前提下,如果數據庫沒有表,那就開始建表了哦!

char *error;?

const char *createSql="create table(id integer primary key autoincrement, name text)";

if (sqlite3_exec(database, createSql, NULL, NULL, &error)==SQLITE_OK) {?

? ? ? ? NSLog(@"create table is ok.");?

}

else

{

? ? ? ?NSLog(@"error: %s",error);

? ? ? ?sqlite3_free(error);//每次使用完畢清空error字符串,提供給下一次使用

}

?

建表完成之后,就開始插入記錄:

?

const char *insertSql="insert into a person (name) values(‘gg’)";?

if (sqlite3_exec(database, insertSql, NULL, NULL, &error)==SQLITE_OK) {?

? ? ? ? NSLog(@"insert operation is ok.");?

}

?

else

{

? ? ? ?NSLog(@"error: %s",error);

? ? ? ?sqlite3_free(error);//每次使用完畢清空error字符串,提供給下一次使用

}?

?

下一步,查詢記錄:

?

const char *selectSql="select id,name from a person";?

sqlite3_stmt *statement;?

if (sqlite3_prepare_v2(database,selectSql, -1, &statement, nil)==SQLITE_OK) {?

? ? ? ? NSLog(@"select operation is ok.");?

}

else

{

? ? ? ?NSLog(@"error: %s",error);

? ? ? ?sqlite3_free(error);

}?

while(sqlite3_step(statement)==SQLITE_ROW) {?

int _id=sqlite3_column_int(statement, 0);?

NSString *name=(char*)sqlite3_column_text(statement, 1);?

NSLog(@"row>>id %i, name %s",_id,name);?

}

sqlite3_finalize(statement);

?

最后,關閉數據庫:

?

sqlite3_close(database);?

?

注意:寫入數據庫,字符串可以采用char方式,而從數據庫中取出char類型,當char類型有表示中文字符時,會出現亂碼。這是因為數據庫默認使用ascII編碼方式。所以要想正確從數據庫中取出中文,需要用NSString來接收從數據庫取出的字符串。

?

CoreData

?

Core Data使用起來相對直接使用SQLite3的API而言更加的面向對象,操作過程通常分為以下幾個步驟:

?

1.創建管理上下文

?

創建管理上下可以細分為:加載模型文件->指定數據存儲路徑->創建對應數據類型的存儲->創建管理對象上下方并指定存儲。

?

經過這幾個步驟之后可以得到管理對象上下文NSManagedObjectContext,以后所有的數據操作都由此對象負責。同時如果是第一次創建上下文,Core Data會自動創建存儲文件(例如這里使用SQLite3存儲),并且根據模型對象創建對應的表結構。下圖為第一次運行生成的數據庫及相關映射文件:

?

為了方便后面使用,NSManagedObjectContext對象可以作為單例或靜態屬性來保存,下面是創建的管理對象上下文的主要代碼:

?

-(NSManagedObjectContext *)createDbContext{

? ? NSManagedObjectContext *context;

? ? //打開模型文件,參數為nil則打開包中所有模型文件并合并成一個

? ? NSManagedObjectModel *model=[NSManagedObjectModel mergedModelFromBundles:nil];

? ? //創建解析器

? ? NSPersistentStoreCoordinator *storeCoordinator=[[NSPersistentStoreCoordinator alloc]initWithManagedObjectModel:model];

? ? //創建數據庫保存路徑

? ? NSString *dir=[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject];

? ? NSLog(@"%@",dir);

? ? NSString *path=[dir stringByAppendingPathComponent:@"myDatabase.db"];

? ? NSURL *url=[NSURL fileURLWithPath:path];

? ? //添加SQLite持久存儲到解析器

? ? NSError *error;

? ? [storeCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:url options:nil error:&error];

? ? if(error){

? ? ? ? NSLog(@"數據庫打開失敗!錯誤:%@",error.localizedDescription);

? ? }else{

? ? ? ? context=[[NSManagedObjectContext alloc]init];

? ? ? ? context.persistentStoreCoordinator=storeCoordinator;

? ? ? ? NSLog(@"數據庫打開成功!");

? ? }

? ? return context;

}

?

2.查詢數據?

?

對于有條件的查詢,在Core Data中是通過謂詞來實現的。首先創建一個請求,然后設置請求條件,最后調用上下文執行請求的方法。

?

-(void)addUserWithName:(NSString *)name screenName:(NSString *)screenName profileImageUrl:(NSString *)profileImageUrl mbtype:(NSString *)mbtype city:(NSString *)city{

? ? //添加一個對象

? ? User *us= [NSEntityDescription insertNewObjectForEntityForName:@"User" inManagedObjectContext:self.context];

? ? us.name=name;

? ? us.screenName=screenName;

? ? us.profileImageUrl=profileImageUrl;

? ? us.mbtype=mbtype;

? ? us.city=city;

? ? NSError *error;

? ? //保存上下文

? ? if (![self.context save:&error]) {

? ? ? ? NSLog(@"添加過程中發生錯誤,錯誤信息:%@!",error.localizedDescription);

? ? }

}

?

如果有多個條件,只要使用謂詞組合即可,那么對于關聯對象條件怎么查詢呢?這里分為兩種情況進行介紹:

?

a.查找一個對象只有唯一一個關聯對象的情況,例如查找用戶名為“Binger”的微博(一個微博只能屬于一個用戶),通過 -

?

(NSArray )getStatusesByUserName:(NSString )name{?

NSFetchRequest *request=[NSFetchRequest fetchRequestWithEntityName:@”Status”];?

request.predicate=[NSPredicate predicateWithFormat:@”user.name=%@”,name];?

NSArray *array=[self.context executeFetchRequest:request error:nil];?

return array; }ata生成的SQL語句會發現其實就是把Status表和User表進行了關聯查詢(JOIN連接)。

?

b.查找一個對象有多個關聯對象的情況,例如查找發送微博內容中包含“Watch”并且用戶昵稱為“小娜”的用戶(一個用戶有多條微博),此時可以充分利用謂詞進行

?

-(NSArray )getUsersByStatusText:(NSString )text screenName:(NSString *)screenName{?

NSFetchRequest *request=[NSFetchRequest fetchRequestWithEntityName:@”Status”];?

request.predicate=[NSPredicate predicateWithFormat:@”text LIKE ‘Watch‘”,text];?

NSArray *statuses=[self.context executeFetchRequest:request error:nil];

?

NSPredicate *userPredicate= [NSPredicate predicateWithFormat:@"user.screenName=%@",screenName];

NSArray *users= [statuses filteredArrayUsingPredicate:userPredicate];

return users;

?

如果單純查找微博中包含“Watch”的用戶,直接查出對應的微博,然后通過每個微博的user屬性即可獲得用戶,此時就不用使用額外的謂詞過濾條件。

?

3.插入數據

?

插入數據需要調用實體描述對象NSEntityDescription返回一個實體對象,然后設置對象屬性,最后保存當前上下文即可。這里需要注意,增、刪、改操作完最后必須調用管理對象上下文的保存方法,否則操作不會執行。

?

-(void)addUserWithName:(NSString *)name screenName:(NSString *)screenName profileImageUrl:(NSString *)profileImageUrl mbtype:(NSString *)mbtype city:(NSString *)city{

? ? //添加一個對象

? ? User *us= [NSEntityDescription insertNewObjectForEntityForName:@"User" inManagedObjectContext:self.context];

? ? us.name=name;

? ? us.screenName=screenName;

? ? us.profileImageUrl=profileImageUrl;

? ? us.mbtype=mbtype;

? ? us.city=city;

? ? NSError *error;

? ? //保存上下文

? ? if (![self.context save:&error]) {

? ? ? ? NSLog(@"添加過程中發生錯誤,錯誤信息:%@!",error.localizedDescription);

? ? }

}

?

4.刪除數據

?

刪除數據可以直接調用管理對象上下文的deleteObject方法,刪除完保存上下文即可。注意,刪除數據前必須先查詢到對應對象。

?

-(void)removeUser:(User *)user{

? ? [self.context deleteObject:user];

? ? NSError *error;

? ? if (![self.context save:&error]) {

? ? ? ? NSLog(@"刪除過程中發生錯誤,錯誤信息:%@!",error.localizedDescription);

? ? }

}

?

5.修改數據

?

修改數據首先也是取出對應的實體對象,然后通過修改對象的屬性,最后保存上下文。

?

-(void)modifyUserWithName:(NSString *)name screenName:(NSString *)screenName profileImageUrl:(NSString *)profileImageUrl mbtype:(NSString *)mbtype city:(NSString *)city{

? ? User *us=[self getUserByName:name];

? ? us.screenName=screenName;

? ? us.profileImageUrl=profileImageUrl;

? ? us.mbtype=mbtype;

? ? us.city=city;

? ? NSError *error;

? ? if (![self.context save:&error]) {

? ? ? ? NSLog(@"修改過程中發生錯誤,錯誤信息:%@",error.localizedDescription);

? ? }

}

?

調試?

?

雖然Core Data(如果使用SQLite數據庫)操作最終轉換為SQL操作,但是調試起來卻不想操作SQL那么方便。特別是對于初學者而言經常出現查詢報錯的問題,如果能看到最終生成的SQL語句自然對于調試很有幫助。事實上在Xcode中是支持Core Data調試的,具體操作:Product-Scheme-Edit Scheme-Run-Arguments中依次添加兩個參數(注意參數順序不能錯):-com.apple.CoreData.SQLDebug、1。然后在運行程序過程中如果操作了數據庫就會將SQL語句打印在輸出面板。

?

轉載于:https://www.cnblogs.com/fengmin/p/5464392.html

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/396079.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/396079.shtml
英文地址,請注明出處:http://en.pswp.cn/news/396079.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Oracle中Date和Timestamp的區別

Date和Timestamp精度不一樣&#xff1a; 01&#xff09;Timestamp精確到了秒的小數點&#xff08;如&#xff1a;2018-11-13 16:40:03.698&#xff09;&#xff1b; 02&#xff09;Date只精確到整數的秒&#xff08;如&#xff1a;2018-11-13 16:40:03&#xff09; 轉載于:http…

table偏見和HTML仇外心理

by Anthony Ng由Anthony Ng <table>偏見和HTML仇外心理 (<table> prejudice and HTML xenophobia) I was looking over some HTML with a student the other day when we stumbled onto a <table>.前幾天&#xff0c;當我偶然發現一個<table>時&#…

回滾機制_【巨杉數據庫SequoiaDB】巨杉 Tech | 并發性與鎖機制解析與實踐

01概述數據庫是一個多用戶使用的共享資源。當多個用戶并發地存取數據時&#xff0c;在數據庫中就會產生多個事務同時存取同一數據的情況。若對并發操作不加控制就可能會讀取和存儲不正確的數據&#xff0c;破壞數據庫的一致性。加鎖是實現數據庫并發控制的一個非常重要的技術。…

Android系統源碼學習——源碼目錄結構介紹

2019獨角獸企業重金招聘Python工程師標準>>> Android 4.0源碼目錄結構: 本文介紹Android源碼目錄結構&#xff0c;以便讀者理清Android編譯系統核心代碼在Android源代碼的位置。 Android源碼體積非常龐大&#xff0c;由Dalvik虛擬機、Linux內核、編譯系統、框架代碼…

簡答題c語言文件操作順序,計算機基礎與程序設計2012年4月真題試題(02275)

計算機基礎與程序設計2012年4月真題試題與答案解析(02275)計算機基礎與程序設計2012年4月真題試題與答案解析(02275)&#xff0c;本試卷總共100分。一、單項選擇題(本大題共20小題.每小題1分&#xff0c;共20分)在每小題列出的四個備選項中只有一個是符合題目要求的&#xff0c…

匯編實驗3

1.運行如下代碼&#xff1a; assume cs:codecode segment mov ah,2 mov dl,3 add dl,30h int 21h mov ah,2 mov dl,6 add dl,30h int 21h mov ah,4ch int 21hcode endsend 進行匯編運行之后結果為&#xff1a; 將第四行和第九行的寄存器dl的值修改之后代碼如下&#xff1a; a…

聽了一堂《**學院》的課,我也是醉了

這還是首席講師的ppt&#xff0c;這說話咋感覺&#xff0c;不像是技術出身&#xff0c;反倒是MongoDB的銷售人員呢。 這說話&#xff0c;不大講相對&#xff0c;凈他媽的 絕對&#xff0c;這水平&#xff0c;我真醉了。 這牛逼吹得&#xff0c;嘖嘖嘖。 我還是看書吧。 轉載于:…

react 組件引用組件_React Elements VS React組件

react 組件引用組件A few months ago I posted to Twitter what I thought was a simple question:幾個月前&#xff0c;我在Twitter上發布了一個我認為簡單的問題&#xff1a; What surprised me wasn’t the joint confusion around this question, but rather the amount o…

appium 環境搭建(不推薦安裝此版本appium,推薦安裝appium desktop)

一&#xff1a;安裝node.js 1、雙擊這個軟件 2、一鍵安裝&#xff0c;全都下一步&#xff0c;不要私自更改安裝路徑 3、打開cmd&#xff0c;輸入npm&#xff0c;出現如下截圖表示成功 二&#xff1a;安裝appium 1、雙擊appium-installer.exe 2、一鍵安裝&#xff0c;全都下一步…

二級c語言上機題庫及解析,2013年計算機二級C語言上機題庫及答案解析(3)

填空題給定程序中&#xff0c;函數fun的功能是:在形參ss所指字符串數組中&#xff0c;查找含有形參substr所指子串的所有字符串并輸出&#xff0c;若沒找到則輸出相應信息。ss所指字符串數組中共有N個字符串&#xff0c;且串長小于M。程序中庫函數strstr(s1, s2)的功能是在 s1串…

js 數組遍歷符合條件跳出循環體_C++模擬面試:從數組“緊湊”操作說開來

面試官自來也去掉一個字符串中的空格。假設用C語言來解答&#xff0c;字符串是char數組。O(n)時間復雜度實現不難&#xff0c;比如額外申請一個新數組&#xff0c;然后遍歷一遍字符串&#xff0c;將符合條件的字符存儲到新數組中&#xff0c;實現起來很簡單。但這顯然不能讓面試…

項目NABCD的分析

N&#xff1a;你的創意解決了用戶的什么需求 本項目解決了在校大學生和社會工程人士在計算一些工程測量中的需求&#xff0c; 可以通過自己提供的一些測得的已知數據來推算出自己想要的數據結果&#xff0c; 比用戶自己手動計算更有效更快更節省時間 A&#xff1a;有什么招數來…

git 命令git 地址_這是我上周使用的所有Git命令及其作用。

git 命令git 地址by Sam Corcos由Sam Corcos 這是我上周使用的所有Git命令及其作用。 (Here are all the Git commands I used last week, and what they do.) Like most newbies, I started out searching StackOverflow for Git commands, then copy-pasting answers, witho…

兩個隊列實現一個棧思路c語言,兩個棧實現隊列功能C語言實現能運行!

#include#includetypedef struct sq{char *ps;int top;int Maxsize;}stack;void initstack(stack *s,int ms){s->ps(char*)malloc(ms*sizeof(char));s->top-1;s->Maxsizems;};void push(stack *s,char val){if(s->tops->Maxsize-1){printf("棧已滿\n"…

基本入門程序編寫格式和注意事項

在安裝好JDK后聯系程序的基本寫法。1、先創建記事本&#xff0c;如果有超級記事本如:notepad、ultraedit、editplus等更好。重命名把記事本后面的后綴名改為.java 但是值得注意的是要看看自己創建的記事本文檔是否是隱藏后綴名的。要是有設置隱藏的就取消隱藏&#xff0c;以免混…

.dll文件存在但是不顯示_一招巧妙解決U盤內文件明明存在,打開U盤而內容卻不顯示的問題...

大家可能都遇到過這種情況&#xff0c;就是說U盤中明明有文件&#xff0c;但是插在電腦上就是什么文件都沒有&#xff0c;一片空白&#xff0c;這樣的問題對于那些對文件很重要且僅保存了1份的人來說是很.kongbu.&#xff0c;因為U盤中的內容都是命根子。給大家介紹絕對有用的解…

《java入門第一季》之面向對象(包概述)

由于eclipse等ide的強大功能&#xff0c;使得建包&#xff0c;導包用一些快捷鍵就能完成。這里對包的概念做稍微的敘述&#xff0c;了解即可&#xff1a; 分包后使得項目更加清晰&#xff0c;提高代碼維護性。 包&#xff1a; A:其實就是文件夾 B:作用 …

Vue 框架-05-動態綁定 css 樣式

Vue 框架-05-動態綁定 css 樣式 今天的小實例是關于 Vue 框架動態綁定 css 樣式&#xff0c;這也是非常常用的一個部分 首先說一下 動態綁定&#xff0c;相對的大家都知道靜態綁定&#xff0c;靜態綁定的話&#xff0c;直接加 class“”就可以了&#xff0c;使用 Vue 呢之前也介…

ember.js_如何設置基本的Ember.js應用

ember.jsby Tracy Lee | ladyleet特雷西李(Tracy Lee)| Ladyleet 如何設置基本的Ember.js應用 (How to set up a Basic Ember.js app) So, you want to test out Ember, eh? This article will walk through building a basic app.所以&#xff0c;您想測試Ember&#xff0c;…

分數轉小數C語言,這是把小數轉換成分數的程序,可是輸入0.6666無限循環

該樓層疑似違規已被系統折疊 隱藏此樓查看此樓#include int main(){double a;scanf("%lf", &a);輸入小數int b, c 0, d 0;double b1 a;do{b1 *10;b (int)b1;printf("%d\n", b);if(b%10!0){c;if(d>0){c d;d 0;}}else{d;}}while(d<5);printf("…