1. 程式人生 > >資料本地化儲存

資料本地化儲存

在程式開發中,資料層永遠是程式的核心結構之一。我們將現實事物進行抽象,使之變成一個個資料。對這些資料的加工處理是程式碼中能體現技術水平的一大模組,比如資料的請求、解析、快取、持久化等等。適當的對資料進行持久化儲存可以實現應用的離線功能,以此提高使用者體驗。在iOS開發中,蘋果提供了四種持久化方案供我們選擇。這些方案分別包括屬性列表(plist)、資料歸檔(NSKeyedValueArchiver/NSUserDefaults)、資料庫(sqlite)和coreData等,它們的區別如下

  • 屬性列表

屬性列表是一種明文的輕量級儲存方式,其儲存格式有多種,最常規格式為XML格式。在我們建立一個新的專案的時候,Xcode會自動生成一個info.plist檔案用來儲存專案的部分系統設定。plist只能用陣列(NSArray)或者字典(NSDictionary)進行讀取,由於屬性列表本身不加密,所以安全性幾乎可以說為零。因為,屬性列表正常用於儲存少量的並且不重要的資料。

在程式啟動後,系統會自動建立一個NSUserDefaults的單例物件,我們可以獲取這個單例來儲存少量的資料,它會將輸出儲存在.plist格式的檔案中。其優點是像字典一樣的賦值方式方便簡單,但缺點是無法儲存自定義的資料。

  • 資料歸檔/序列化

與屬性列表相反,同樣作為輕量級儲存的持久化方案,資料歸檔是進行加密處理的,資料在經過歸檔處理會轉換成二進位制資料,所以安全性要遠遠高於屬性列表。另外使用歸檔方式,我們可以將複雜的物件寫入檔案中,並且不管新增多少物件,將物件寫入磁碟的方式都是一樣的。

使用NSKeyedArchiver對自定義的資料進行序列化,並且儲存在沙盒目錄下。使用這種歸檔的前提是讓儲存的資料模型遵守NSCoding協議並且實現其兩個協議方法。(當然,如果為了更加安全的儲存,也可以遵守NSSecureCoding協議,這是iOS6之後新增的特性)

  • 資料庫

sqlite是一個輕量級、跨平臺的小型資料庫,其擁有可移植性高、有著和MySql幾乎相同的資料庫語句以及無需伺服器即可使用的優點:

​ 可以儲存大量的資料,儲存和檢索的速度非常快;

​ 能對資料進行大量的聚合,這樣比起使用物件來進行這些操作要快。

當然,它也具有明顯的缺點:

​ 它沒有提供資料庫的建立方式;

​ 它基於C語言框架設計,沒有面向物件的API,所以使用起來比較麻煩;

​ 複雜的資料模型的資料建表相對而言比較麻煩。

當然,我們也可以使用基於sqlite封裝的開源資料庫FMDB來減少使用sqlite的工作量

  • coreData

coreData是蘋果官方iOS5之後推出的綜合型資料庫,其使用了ORM(Object Relational Mapping)物件關係對映技術,將物件轉換成資料,儲存在本地資料庫中。coreData為了提高效率,甚至將資料儲存在不同的資料庫中,且在使用的時候將本地資料放到記憶體中使得訪問速度更快。我們可以選擇coreData的資料儲存方式,包括sqlite、xml等格式。但也正是coreData 是完全面向物件的,其在執行效率上比不上原生的資料庫。除此之外,coreData擁有資料驗證、undo等其他功能,在功能上是四種持久化方案最多的。

上面已經分別介紹了四種方案的優缺點,在開發中,並沒有說哪種持久化方案是最好的,只能說在不同開發場景下,最適合使用的持久化方案。下面我們將用程式碼實戰的方式對這些持久方案進行更加詳細的瞭解

屬性列表

在我們每次建立新的專案的時候,Xcode幫助我們生成了Info.plist檔案,裡面儲存了關於專案名字、版本、bundle id等等關鍵資訊,這個plist檔案也是逆向工程(越獄)中獲取app資料的重要檔案。OK,那麼什麼情況下用plist儲存呢?打個比方,最近在實現公司專案業務的時候,需要使用選擇器(UIPickerView)給使用者選擇所在城市。對於城市資料,並沒有加密的必要,而且這時候使用plist會達到更高一些的效率。既然已經知道需要的資料,那麼很容易就得得出省-市這樣的一對多的資料型別,我們的plist使用字典,將省份作為key,儲存對應的城市的陣列作為value

1、建立plist檔案。New Files -> iOS -> Resource -> Property List -> Next

2、給這個plist檔案命名cities,點選Create後建立好。然後我們選中Root,預設已經是字典的資料結構儲存了,我們點選Root右邊的加號新增一些鍵值對,然後修改左邊的key的文字,並且將每一個key對應的value設定為Array陣列

3、按照在字典中新增鍵值對的方式,在設定好每個key對應的型別之後,移動到每一行上面點擊出現的+給每一個省份新增陣列元素,並且賦值,最終效果圖如下

當然,像這種城市的plist檔案百度一下就可以找到,但是建立plist的方式相信大家看完之後也就明白了。從plist讀取資料的方式也很簡單,蘋果把讀取的方法封裝在NSArray跟NSDictionary中,讀取步驟分為兩步:

1、獲取plist檔案路徑:

NSString * filePath = [[NSBundle mainBundle] pathForResource: @"cities" ofType: @"plist"];

2、通過陣列或者字典的構造器方法建立容器:

NSDictionary * dict = [NSDictionary dictionaryWithContentsOfFile: filePath];

//NSArray * array = [NSArray arrayWithContentsOfFile: filePath];

實現選擇器的大概思路是用兩個陣列分別儲存省份以及當前選中省份的城市陣列,然後在滑動pickerView的回撥事件中根據選中的省份更新城市資料來源。

另外,還有一個NSUserDefault,其支援的資料格式有:NSNumber(Integer、Float、Double等)、NSString、NSDate、NSArray(成員必須也是支援的格式型別)、NSDictionary(同NSArray)。其使用和讀取也是非常的簡單,像字典一樣的存取方式

讀取:NSString * str = [[NSUserDefaults standardUserDefaults] valueForKey: @"str"];

儲存:[[NSUserDefaults standardUserDefaults] setValue: @"str" forKey: @"str"];

同樣的也可以使用setObject:forKey:或者objectForKey:等字典存取方法

資料歸檔/資料序列化

更多時候,NSUserDefaults已經提供了儲存簡單變數的持久化方案。然而,當我們想要儲存複雜的自定義資料時,NSUserDefaults無法為我們提供更多的幫助,這時候考慮的是另外的持久化方案。而並非所有的程式都需要查詢資料、資料遷移這些操作,而且也並非所有的資料都有複雜的關係圖。這時候,資料歸檔絕對是不二的選擇。

我們使用archive格式的檔案將歸檔化的資料儲存在沙盒目錄下,這種格式的檔案讀取出來是二進位制資料(NSData),然後使用NSKeyedUnarchiver類對資料進行反序列化。假設當前需要進行持久化儲存的是一款離線遊戲,我需要儲存遊戲前十名的成績、成績持有者和記錄建立時間這些資料,那麼相對應的LXDGameRecord類宣告如下,其必須遵循NSCoding或NSSecureCoding協議之一:

@interface LXDGameRecord : NSObject

@property (nonatomic, copy) NSString * userName;

@property (nonatomic, strong) NSDate * createDate;

@property (nonatomic, strong) NSNumber * score;

@end

@implementation LXDGameRecord

/** 協議方法-對資料進行反序列化並讀取*/

- (id)initWithCoder: (NSCoder *)aDecoder

{

self.userName = [aDecoder decodeObjectForKey: kUserNameKey];

self.createDate = [aDecoder decodeObjectForKey: kCreateDateKey];

self.score = [aDecoder decodeObjectForKey: kScoreKey];

}

/** 協議方法-對資料進行序列化*/

- (void)encodeWithCoder:(NSCoder *)aCoder

{

[aCoder encodeObject: self.userName forKey: kUserNameKey];

[aCoder encodeObject: self.createDate forKey: kCreateDateKey];

[aCoder encodeObject: self.score forKey: kScoreKey];

}

@end

對於任意自定義型別的資料,只要遵循上面的步驟,就能對資料進行歸檔了。這裡還要講一下一個小技巧:使用static修飾來替代巨集定義。上面的序列化中,我們可以看到NSCoding的協議方法中對資料進行序列化並且使用一個key來儲存它。正常情況下我們可以使用巨集來定義key,但是過多的巨集定義在編譯時也會造成大量的損耗。這時候可以使用static定義靜態變數來取代巨集定義。

static NSString * const kUserNameKey = @"userName";

讓自定義的資料遵循NSCoding協議後,我們就能使用NSKeyedArchiver和NSKeyedUnarchiver來對持久化的資料進行存取操作了

/*使用NSKeyedUnArchiver對資料反序列化並讀取*/

NSString * filePath = [self applicationDocumentStorage];

NSData * fileData = [NSData dataWithContentFile: filePath];

NSKeyedUnarchiver * unarchiver = [[NSKeyedArchiver alloc] initForReadingWithData: fileData];

NSArray * datas = [unarchiver decodeObjectForKey: kArchiveKey]; //反序列化

[unarchiver finishDecoding]; //完成反序列化

/** 使用NSKeyedArchiver對資料進行序列化*/

NSMutableData * recordData = [NSMutableData data];

NSKeyedArchiver * archiver = [[NSKeyedArchiver alloc] initForWritingWithMutableData: recordData];

[archiver encodeObject: datas forKey: kArchiveKey]; //序列化資料

[archiver finishEncoding]; //完成序列化操作

[recordData writeToFile: filePath]; //序列化完成後寫入本地磁碟

下面展示的是曾經學習時仿qq登入介面中使用資料歸檔儲存使用者註冊的賬號(由於沒有伺服器,只能儲存在本地),其中使用者的資料包括了暱稱、頭像圖片、使用者賬號、密碼這些資料,在輸入完賬號自己從代理方法中訪問這些資料並獲取使用者的頭像

sqlite資料庫

基於sqlite封裝的FMDB幾乎是我工作中最常用到的持久化方案。在實際開發中,sqlite佔用的記憶體非常非常的少,在嵌入式裝置中,可能只需要幾百K即可。其次,它的速度非常的快,幾乎快過所有其他的資料庫。當然啦,開始使用資料庫進行開發之前,你得了解sqlite支援的資料型別,包括NULL(空值)、Integer(整型)、Real(實數)、Text(字串)、BLOB(二進位制)

在使用sqlite前要匯入libsqlite3.0框架,然後匯入標頭檔案。其操作步驟大致如下:

  • 使用sqlite3open(const char filename, sqlite3 *ppDb)方法開啟指定路徑下的資料庫存入到建立的資料庫變數中,如果存在資料庫就開啟。不存在資料庫則關閉。成功開啟資料庫的時候會返回SQLITEOK

​ static NSString * const dbName = @"myDBText.db";

​ NSString * documentDirectory = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject];

​ NSString * filePath = [documentDirectory stringByAppendingPathComponent: dbName];

​ if (SQLITE_OK == sqlite3_open(filePath.UTF8String, &_database)) {

​ NSLog(@"資料庫開啟成功!");

} else {

​ NSLog(@資料庫開啟失敗!“);

}

  • 執行SQL語句。有返回值的語句執行第四步,無返回值的執行第三步

  • 對於無返回值的SQL語句(包括增刪改等操作)通過sqlite3_exec()函式執行

char * error;

NSString * executeSql = @"select * from table student where name like '張%'";

if (SQLITE_OK != sqlite3_exec(_database, executeSql.UTF8String, NULL, NULL, &error)) {

NSLog(@"執行SQL語句過程發生錯誤!");

}

  • 對於有返回值的SQL語句首先通過sqlite3preparev2()進行語法評估,然後通過sqlite3step()函式依次取出查詢結果的每一行資料,最後通過sqlite3column_type()方法獲取對應列的資料

NSMutableArray * rows = [NSMutableArray new];

sqlite3_stmt * stmt; //檢查語法正確性

if (SQLITE_OK == sqlite3_prepare_v2(_database, executeSql.UTF8String, -1, &stmt, NULL)) {

//單步執行sql語句

while (SQLITE_ROW == sqlite3_step(stmt)) {

int columnCount = sqlite3_column_count(stmt); //獲取列數

NSMutableDictionary * dic = [NSMutableDictionary dictionary];

for (int i = 0; i < columnCount; i++) {

const char * name = sqlite3_column_name(stmt, i); //取到列名

const unsigned char * value = sqlite3_column_text(stmt, i); //取得某列的值

dic[[NSString stringWithUTF8String: name]] = [NSString stringWithUTF8String: (const char *)value];

}

[rows addObject: dic];

}

}

sqlite3_finalize(stmt);

sqlite的原生語句對於開發者而言有時是一個災難,在熟練使用之前,我們很難保證資料庫的語句和執行程式碼沒有任何問題。對此,我們可以在github上面找到基於sqlite3封裝的FMDB,它提供了特有的機制來保證資料庫訪問是執行緒安全的,至於使用方法在網上一搜一大把教程,這裡就不在細說。但是,使用FMDB有一個要注意的問題是——當我們把圖片轉換成二進位制資料儲存在資料庫中的時候,再次讀取出這個二進位制資料初始化成圖片的時候會出錯誤,無法正常轉換成影象。解決方案詳見這裡http://mobile.51cto.com/hot-405287.htm

coreData

coreData是iOS5之後蘋果推出的資料持久化框架,其提供了ORM的功能,將物件和資料相互轉換。其中,它提供了包括sqlite、xml、plist等本地儲存檔案,預設使用sqlite進行儲存。coreData具有兩個模型:關係模型和物件模型,關係模型即是資料庫,物件模型為OC物件。其關係圖如下

由於我們不需要關心資料的儲存,coreData使用起來算是最簡單的持久化方案。要使用coreData有兩個方式,一個是在建立專案的時候勾選use core data,另一個則是手動建立。在這裡我們要講解的是前者建立的方式

1、建立新專案勾選使用coreData

2、建立關係模型,在這裡我建立的模型名字是LXDCoreDataDemo

3、在建立的關係模型中新增實體,命名為Person,並且新增三個欄位:name、age、score

到了這裡我們的實體模型就建立好了,接下來就是通過NSManagedObject來將實體模型轉換成物件。通過從coreData取出的物件,全部都是繼承自NSManagedObject的子類。那麼我們需要根據當前的關係模型來建立Person類

選擇LXDCoreDataDemo -> Next -> Person -> Create,我們就建立好了Person,這時候三個成員屬性都會自動新增完成

接著我使用故事板建立了下面的檢視,在我點選按鈕的時候往資料庫中插入新的person資料

在執行操作的類實現檔案中,我們要加入AppDelegate和Person的標頭檔案,因為在建立專案的時候如果我們勾選了use core data的選項,appDelegate檔案中會幫我們生成用於管理、儲存這些模型的物件,我們可以通過新增標頭檔案來使用。插入資料的程式碼如下:

//先取出coredata上下文管理者

AppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];

NSManagedObjectContext *context = appDelegate.managedObjectContext;

//儲存新資料

Person *person = [NSEntityDescription insertNewObjectForEntityForName: @"Person" inManagedObjectContext: context];

person.name = _userName.text;

person.name = _userScore.text;

person.age = @([_userAge.text integerValue]);

[appDelegate saveContext];

//查詢所有資料

NSError *error;

NSFetchRequest *request = [NSFetchRequest new];

NSEntityDescription *entity = [NSEntityDescription entityForName: @"Person" inManagedObjectContext: context];

[request setEntity: entity];

NSArray *results = [[context executeFetchRequest: request error: &error] copy];

for (Person *p in results) {

NSLog(@"%@, %@, %@", p.name, p.age, p.score);

}

想要對coreData有更深入的瞭解可以購買這本Core Data應用開發實踐指南,裡面詳細講述了coreData的各種使用技巧。

相關推薦

資料本地化儲存

在程式開發中,資料層永遠是程式的核心結構之一。我們將現實事物進行抽象,使之變成一個個資料。對這些資料的加工處理是程式碼中能體現技術水平的一大模組,比如資料的請求、解析、快取、持久化等等。適當的對資料進行持久化儲存可以實現應用的離線功能,以此提高使用者體驗。在iOS開發中,蘋

iOS開發————資料本地化儲存方式之Keychain

iOS的keychain服務提供了一種安全的儲存私密資訊(密碼,序列號,證書等)的方式,每個ios程式都有一個獨立的keychain儲存。相對於NSUserDefaults、檔案儲存等一般方式,keychain儲存更為安全,而且keychain裡儲存的資訊不會因App被刪除

《Hadoop 權威指南 - 大資料儲存與分析》學習筆記

第一章 初識Hadoop 1.2 資料的儲存與分析 對多個硬碟中的資料並行進行讀/寫資料,有以下兩個重要問題: 硬體故障問題。解決方案:複製(replication),系統儲存資料的副本(replica)。 以某種方式結合大部分資料來共同完成分析。MapReduce

微信小程式中資料儲存和獲取

/儲存資料     try {       wx.setStorageSync('key',this.data.radioCheckVal2)  //key表示data中的引數

List介面派系,資料儲存結構

List介面派系,繼承Collection介面,下面有很對實現類,如ArrayList,LinkedList list是有序集合,怎麼存的,怎麼拿出來,可以對列表中的元素位置進行準確控制(具有索引) list集合允許儲存重複元素,set不允許 List介面中的抽象方法, 有一部分和他

BCP SQL匯出EXCEL常見問題及解決方法;資料匯出儲存過程

一、‘xp_cmdshell’的啟用 SQL Server阻止了對元件‘xp_cmdshell’的過程‘sys.xp_cmdshell’的訪問。因為此元件已作為此服務囂安全配置的一部分而被關 閉。系統管理員可以通過使用sp_configure啟用‘xp_cmdshell’。有關啟用‘xp_cmdshell’

java學習實驗隨筆-------如何從鍵盤輸入資料儲存到陣列中

從鍵盤輸入並儲存 public static void main(String[] args) {  Scanner reader = new Scanner(System.in);       /使用Scanner庫進行輸入操作 int[] a = ne

資料導論(5)——大資料儲存(分散式、NoSQL、叢集、CAP、ACID、BASE)

  大資料從獲取到分析的各個階段都可能會涉及到資料集的儲存,考慮到大資料有別於傳統資料集,因此大資料儲存技術有別於傳統儲存技術。大資料一般通過分散式系統、NoSQL資料庫等方式(還有云資料庫)進行儲存。同時涉及到以下幾個新理念。 本篇summary主要圍繞以下三方面內容: 大資料儲存方案

資料儲存與進位制轉換

python的發展史 http://www.cnblogs.com/vamei/archive/2013/02/06/2892628.html python的優缺點 http://blog.csdn.net/summerhust/article

資料儲存

資料的儲存 思考:為什麼使用計算機? 儲存資料,計算資料 思考:資料存在哪裡? 資料儲存在記憶體裡 思考:資料怎麼在記憶體裡儲存的? 首先弄明白怎麼儲存數字 10 10.5 “sunck is a good man” 記憶體: 抽象:一個開關,有

資料中心儲存系統故障的處理方式

儲存系統作為如今大資料雲端計算時代的根基,它的穩定才是支撐起如此海量資料的根本。所以各大相關行業對於這部分非常重視,所以在此針對儲存系統的故障處置簡單提出自己學習總結的一些思路。 首先在處理儲存系統發生的可能故障前必須對於整個儲存系統的架構以及原理有一個

資料儲存

  一、什麼是大資料,本質?         (1)資料的儲存:分散式檔案系統(分散式儲存)-----> HDFS: Hadoop Distributed File System     &nb

MySQL千萬級資料分割槽儲存及查詢優化

本文轉載自:https://www.cnblogs.com/javaIOException/p/7524945.html 作為傳統的關係型資料庫,MySQL因其體積小、速度快、總體擁有成本低受到中小企業的熱捧,但是對於大資料量(百萬級以上)的操作顯得有些力不從心,這裡我結合之前開發的一個web系

ios 資料持久化儲存

說到資料儲存,我們不得不先了解下蘋果的沙盒 、如何獲取沙盒路徑和沙盒目錄下對應的檔案: 一、沙盒(sandbox) 每一個App都有一個儲存空間。iOS系統為每個應用程式建立自己的目錄,每個應用程式只能訪問自己的目錄,不能相互通訊。 沙盒主要包括下面幾個檔案:用模擬器執行 NS

超寬頻訊號高速資料採集儲存系統—取樣率5GSPS,模擬頻寬3GHZ,記錄儲存頻寬6GB/S(西安慕雷電子科技有限公司)

轉:https://blog.csdn.net/u014752194/article/details/23868569?utm_source=blogxgwz7 2018年8月新品https://download.csdn.net/download/mxc5575952/10725451?utm

爬蟲資料儲存

1,Json   class JsonWithEncodingPipeline(object): #自定義json檔案的匯出 def __init__(self): self.file = codecs.open('article.json', 'w', e

資料分散式儲存的部署模式:分離式or超融合

大資料分散式儲存的部署模式:分離式or超融合 資料中心內部系統的核心要求是“穩定可靠”,一是指系統在執行過程中有能力提供連續可靠的服務,長時間無故障執行;二是指當故障發生之後,有能力快速定位,及時排查,故障範圍不蔓延。 分離式部署的方式,使得系統與雲平臺系統相獨立,避免了計算和儲存爭搶CPU

yarn架構調優,資料本地化

yarn的指令 在/home/hadoop/app/hadoop-2.6.0-cdh5.7.0/bin路徑下 ,執行jar包。 yarn jar ./share/hadoop/mapreduce2/hadoop-mapreduce-examples-2.6.0-cdh5.7.0.ja

wamp 中mysql資料儲存中文資料查詢後變成 ’???’

這個問題弄了好長時間,總是會變成??? 每次一查詢就是下面這樣,改了好多東西也不對; 網上查了很多辦法,現在給大家總結一下; 開啟wamp中mysql的配置檔案my.ini 找到下面的位置進行更改 由於網上很多辦法都是在【mysqld】下新增charac

SPARK 資料本地化(spark.locality.wait)

1.概念:task在執行前都會獲取資料的分割槽資訊進行分配,總是會優先將其分配到它要計算的資料所在節點,儘可能的減少網路傳輸 2.過程:一般會預設3s,重試5次的去分配,一旦超時失敗,將會選擇一個比上一個本地級別差的級別再一次分配,如果發生了資料傳輸,那麼task首先通過blockmanager