1. 程式人生 > >iOS本地數據存儲方案匯總

iOS本地數據存儲方案匯總

hybridapp 百度 流量 環境 content 退出 適用於 字段名 表之間

一。

常見存儲方式

  • Plist 格式文件存儲
  • NSUserDefaults 沙盒存儲(個人偏好存儲)
  • 文件讀寫儲存
  • 解歸檔存儲
  • 數據庫存儲
  • Keychain 存儲(可解決設備唯一標識問題)

Demo 示例

  • 文件讀寫操作、沙盒操作、解歸檔操作、依賴 FMDB 的數據庫操作 以及 UIWeb/WKWeb 緩存清理 、NSPredicate 數據篩查等操作 DataStore Git地址
  • 依賴 MagicalRecord 的數據庫操作 CoreDataPro
  • 獲取設備信息 - keychain 存儲 GainRelativeInfo
  • Demo 做筆記使用,持續更新 。。。

數據存儲基礎

  • 作為移動端開發工程師,所需要的數據幾乎全部都是通過網絡獲取,而且網絡請求都有時耗;在網絡好的情況下這種時耗雖然不足考慮,但是一旦網絡環境不好,會很影響產品體驗。網絡環境無法控制,但是對於一些數據不經常變動的網絡請求或沒必要實時更新的數據,我們可以選擇將網絡數據緩存本地,適時更新。

  • 在iOS中涉及存儲方式不外乎這幾種,只是大家各自的分類方式可能有些不同。這裏僅是自己的思考與分類。

  • 了解緩存,有必要先了解一下沙盒這個概念。
    沙盒其實質就是在iOS系統下,每個應用在內存中所對應的儲存空間。
    每個iOS應用都有自己的應用沙盒(文件系統目錄),與其他文件系統隔離,各個沙盒之間相互獨立,而且不能相互訪問(手機沒有越獄的情況下)。

    各個應用程序的沙盒是相互獨立的,在系統內存消耗過高時,系統會收到內存警告並自動將一些退出軟件。這就保證了系統的數據的安全性及系統的穩定性。

  • 一個應用的沙盒目錄如下:

  • Documents 應用程序在運行時生成的一些需要長久保存的數據。
  • Library/Caches 儲存應用程序網絡請求的數據信息(音視頻與圖片等的緩存)。此目錄下的數據不會自動刪除,需要程序員手動清除該目錄下的數據。主要用於保存應用在運行時生成的需要長期使用的數據.一般用於存儲體積較大數據。
  • Library/Preferences 設置應用的一些功能會在該目錄中查找相應設置的信息,該目錄由系統自動管理,通常用來儲存一些基本的應用配置信息,比如賬號密碼,自動登錄等。
  • tmp 保存應用運行時產生的一些臨時數據;應用程序退出、系統空間不夠、手機重啟等情況下都會自動清除該目錄的數據,iTunes或iCloud也不會對其進行備份。無需程序員手動清除該目錄中的數據。
  • SystemData - 近期優化項目數據存儲,發現多了該文件路徑,暫未做過多研究。

Plist 格式文件存儲

  • plist文件,即屬性列表文件。
  • 可以存儲的數據類型有 Array Dictionary String Boolean Date Data Number。
  • 常用於儲存用戶的設置 或 存儲項目中經常用到又不經常修改的數據。
  • 創建 .plist 文件可以使用可視化工具即Xcode ,也可以使用代碼。
  • 不適合存儲大量數據,而且只能存儲基本數據類型。
  • 雖然可以實現 :增 刪 改 查 等操作,但由於數據的存取必須是一次性全部操作,所以性能方面表現並不好。

NSUserDefaults 沙盒存儲(個人偏好存儲)

  • 補充幾個方法:

isSubclassOfClass :參數為類 - 參數類為其子類或本身 ;

isMemberOfClass :參數為實例對象 - 參數所屬類為其本身 ;

isKindOfClass :參數為實例對象 - 參數所屬類為其子類或本身 。

  • 應用程序啟動後,會在沙盒路徑Library -> Preferences 下默認生成以工程bundle為名字的 .plist 文件 , 該方式存儲的數據即存進該文件當中。
  • 常用語存儲用戶的個人偏好設置。
  • 這種方式本質是操作plist文件,所以性能方面的考慮同plist文件數據儲存。

文件讀寫儲存

  • 文件操作可通過單例 NSFileManager 處理。文件存儲的路徑可以代碼設置。
  • 可以存儲大量數據,對數據格式沒有限制。
  • 但由於數據的存取必須是一次性全部操作,所以在頻繁操作數據方面性能欠缺。

解歸檔存儲

  • plist 與 NSUserDefaults(個人偏好設置)兩種類型的儲存只適用於系統自帶的一些常用類型,而且前者必須拿到文件路徑,後者也只能儲存應用的主要信息。
  • 對於開發中自定義的數據模型的儲存,我們可以考慮使用歸檔儲存方案。
  • 歸檔保存數據,文件格式自己可以任意,沒有要求 ; 即便設置為常用的數據格式(如:.c .txt .plist 等)要麽不能打開,要麽打開之後亂碼顯示。
  • 值得註意的是使用歸檔保存的自定義模型需要實現NSCoding協議下的兩個方法。
  • 不適合存儲大量數據,可以存儲自定義的數據模型。
  • 雖然歸檔可以存儲自定義的數據結構,但在大批量處理數據時,性能上仍有所欠缺。
數據庫存儲
  • SQLite : 它是一款輕型的嵌入式數據庫,安卓和ios開發使用的都是SQLite數據庫;占用資源非常的低,在嵌入式設備中,可能只需要幾百K的內存就夠了;而且它的處理速度比Mysql、PostgreSQL這兩款著名的數據庫都還快。

  • FMDB 正式基於 SQLite 開發的一套開源庫。使用時,需要自己寫一些簡單的SQLite語句。

  • CoreData 是蘋果給出的一套基於 SQLite 的數據存儲方案;而且不需要自己寫任何SQLite語句。該功能依賴於 CoreData.framework 框架,該框架已經很好地將數據庫表和字段封裝成了對象和屬性,表之間的一對多、多對多關系則封裝成了對象之間的包含關系。

  • Core Data的強大之處就在於這種關系可以在一個對象更新時,其關聯的對象也會隨著更新,相當於你更新一張表的時候,其關聯的其他表也會隨著更新。Core Data的另外一個特點就是提供了更簡單的性能管理機制,僅提供幾個類就可以管理整個數據庫。由於直接使用蘋果提供的CoreData容易出錯,這裏提供一個很好的三方庫 MagicalRecord 。

  • 關於 Core Data 想吐槽的是,數據存儲功能雖然封裝的很好、功能很強大,但是版本叠代中反復修改數據模型、新增數據模型等問題引起的數據庫遷移問題給開發工作帶來很多不必要的工作;尤其有未解除過該技術的新人加入。

  • Pod 添加 MagicalRecord 依賴庫之後,文件創建 - 數據實體創建 - 數據遷移 :

新建工程 ,Use Core Data 可選可不選,這就給未勾選該項的舊工程使用 Core Data 技術提供可能

創建 .xcdatamodeld 文件

創建實體、新增屬性

創建實體相應的關聯文件

數據庫遷移步驟 1

數據庫遷移步驟 2

緩存系統

APP緩存系統設計簡要記錄(整理中)
HybridAPP(整理優化中)

  • 對大多數 APP 而言,都是 Hybrid 開發,Web 頁與原生同時存在,其中 Web 頁可能是 UIWeb 也可能是 WKWeb 。所以與之相應的緩存系統,應該包括 Web 緩存與 原生接口數據緩存兩部分。

  • Web 緩存有網絡緩存及 Webkit 框架機制內的緩存。這裏也可以依據 URL + 時間戳 自行設計一套緩存。

  • 原生接口部分的數據緩存

    1. 與用戶相關的信息、單個標記符標識等常采用沙盒存儲。
    2. 全局使用到的數據模型,需要永久存儲的話可以考慮歸檔;例如用戶登陸後的個人信息數據模型。
    3. 倘若不涉及大規模數據的增刪改查等操作,可以考慮文件讀寫的方式直接存儲網絡返回的 JSON 對象,借助 YYCache 亦可實現高性能存儲。
    4. 大規模數據的存儲例如帖子、評論、新聞、外賣、商品等可以考慮使用數據庫:FMDB (DataStoreDemo - JXFMDBMOperator)或 Core Data (推薦使用 CoreData 的封裝庫 MagicalRecord);也可以簡單點直接使用 NSPredicate 對數據的篩查操作,簡單直接。

https://www.jianshu.com/p/ca9d8346e7d6

二。

iOS本地數據持久化的幾種類型
iOS本地數據持久化幾種類型的應用場景及使用

一,iOS本地數據持久化的類型:
1,NSUserDefaults
2,plist
3,Keychain(鑰匙串)
4,歸檔
5,沙盒寫入
6,數據庫

二,應用場景
1,NSUserDefaults
用於存儲用戶的偏好設置和用戶信息,如用戶名,是否自動登錄,字體大小等.
數據自動保存在沙盒的Libarary/Preferences目錄下.
NSUserDefaults將輸入的數據儲存在.plist格式的文件下,這種存儲方式就決定了它的安全性幾乎為0,所以不建議存儲一些敏感信息如:用戶密碼,token,加密私鑰等!
它能存儲的數據類型為:NSNumber(NSInteger、float、double),NSString,NSDate,NSArray,NSDictionary,BOOL.
不支持自定義對象的存儲.

NSUserDefaults *userDefault = [NSUserDefaults standardUserDefaults];
    [userDefault setInteger:1 forKey:@"integer"];
    [userDefault setBool:YES forKey:@"BOOl"];
    [userDefault setFloat:6.5 forKey:@"float"];
    [userDefault setObject:@"123" forKey:@"numberString"];
    NSString *numberString = [userDefault objectForKey:@"numberString"];
    BOOL myBool = [userDefault boolForKey:@"BOOl"];
    [userDefault removeObjectForKey:@"float"];
    [userDefault synchronize];

需要註意的問題:
NSUserDefaults存儲的數據都是不可變的,想將可變數據存入需要先轉為不可變才可以存儲.
NSUserDefaults是定時把緩存中的數據寫入磁盤的,而不是即時寫入,為了防止在寫完NSUserDefaults後程序退出導致的數據丟失,可以在寫入數據後使用synchronize強制立即將數據寫入磁盤.

2,plist
即屬性列表文件,全名是Property List,這種文件的擴展名為.plist,因此,通常被叫做plist文件。它是一種用來存儲串行化後的對象的文件,用於存儲程序中經常用到且數據量小而不經常改動的數據。
可以存儲的類型:NSNumber,NSString,NSDate,NSData ,NSArray,NSDictionary,BOOL.
不支持自定義對象的存儲.

plist的創建方式有兩種:command + n 創建和純代碼創建,不同的創建方式使用方法也自然不同

command + n 創建:
創建:(創建方法自行百度)
讀取:

NSString *plistPath = [[NSBundle mainBundle]pathForResource:@"myTest" ofType:@"plist"];
    NSMutableDictionary *dataDic = [NSMutableDictionary dictionaryWithContentsOfFile:plistPath];
    /**如果為數組時*/
//    NSMutableArray *dataArray = [NSMutableArray arrayWithContentsOfFile:plistPath];
    NSLog(@"%@",dataDic);

修改:

[dataDic setObject:@"男" forKey:@"sex"];
[dataDic setObject:@15 forKey:@"age"];
[dataDic writeToFile:plistPath atomically:YES];
NSLog(@"----%@",dataDic);

純代碼創建:
創建:

NSArray *sandBoxPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsPath = [sandBoxPath objectAtIndex:0];
NSString *plistPath = [documentsPath stringByAppendingPathComponent:@"myTestPlist.plist"];
    NSLog(@"%@",plistPath);

寫入:

NSMutableDictionary *dic = [NSMutableDictionary dictionary];
[dic setObject:@"18" forKey:@"age"];
[dic setObject:@"胡楊" forKey:@"name"];
[dic writeToFile:plistPath atomically:YES];

讀取:

NSArray *sandBoxPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *documentsPath = [sandBoxPath objectAtIndex:0];
    NSString *plistPath = [documentsPath stringByAppendingPathComponent:@"myTestPlist.plist"];
    NSLog(@"%@",plistPath);
    
    NSMutableDictionary *dataDic = [NSMutableDictionary dictionaryWithContentsOfFile:plistPath];
    NSLog(@"-----%@",dataDic);

修改:
與command+n方法相同.

需要註意的問題:
如果需要存儲自定義類型的數據需要先進行序列化!

3,Keychain
用於本地重要數據的存儲,將數據加密後存儲在本地更安全.如:密碼,秘鑰,序列號等.當你刪除APP後Keychain存儲的數據不會刪除,所以在重裝App後,Keychain裏的數據還能使用。從ios 3.0開始,跨程序分享keychain變得可行而NSUserDefaults存儲的數據會隨著APP而刪掉.
使用keychain時蘋果官方已經為我們封裝好了文件KeychainItemWrapper,引入即可使用.當然也可是使用其他優秀的第三方的封裝,比如ssKeychain,使用方法如下:
使用方法

4,歸檔(NSKeyedArchiver)
歸檔是iOS開發中數據存儲常用的技巧,歸檔可以直接將對象儲存成文件,把文件讀取成對象。
相對於plist或者userdefault形式,歸檔可以存儲的數據類型更加多樣,並且可以存取自定義對象。對象歸檔的文件是保密的,在磁盤上無法查看文件中的內容,更加安全。
遵守NSCoding協議,並實現該協議中的兩個方法。如果是繼承,則子類一定要重寫那兩個方法。因為子類在存取的時候,會去子類中去找調用的方法,沒找到那麽它就去父類中找,所以最後保存和讀取的時候新增加的屬性會被忽略。需要先調用父類的方法,先初始化父類的,再初始化子類的。
保存數據的文件的後綴名可以隨意命名。
demo

5,沙盒寫入
持久化在Document目錄下,一般存儲非機密數據。當App中涉及到電子書閱讀、聽音樂、看視頻、刷圖片列表等時,推薦使用沙盒存儲。因為這可以極大的節約用戶流量,而且也增強了app的體驗效果.

Application:存放程序源文件,上架前經過數字簽名,上架後不可修改。
Documents: 保存應?運行時生成的需要持久化的數據,iTunes同步設備時會備份該目錄。例如,遊戲應用可將遊戲存檔保存在該目錄。
tmp: 保存應?運行時所需的臨時數據,使?完畢後再將相應的文件從該目錄刪除。應用 沒有運行時,系統也可能會清除該目錄下的文件。iTunes同步設備時不會備份該目錄。
Library/Caches: 保存應用運行時?成的需要持久化的數據,iTunes同步設備時不會備份 該目錄。?一般存儲體積大、不需要備份的非重要數據,比如網絡數據緩存存儲到Caches下
Library/Preference: 保存應用的所有偏好設置,如iOS的Settings(設置) 應?會在該目錄中查找應?的設置信息。iTunes同步設備時會備份該目錄。

// 獲取程序的Home目錄       
NSString  *path = NSHomeDirectory();     

// 獲取Document目錄       
NSString *path = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject;  
     
// 獲取Cache目錄       
NSString *path = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES).firstObject;   
     
// 獲取Library目錄       
NSString *path = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES).firstObject;

// 獲取Tmp目錄       
NSString *path = NSTemporaryDirectory();

寫入:

NSString *path = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES).firstObject;
NSString *filePath = [path stringByAppendingPathComponent:@"test.txt"];
NSLog(@"%@",filePath);
NSArray *array = @[@"1",@"2",@"3"];
    BOOL isSuccess = [array writeToFile:filePath atomically:YES];
    if (isSuccess) {
        NSLog(@"成功");
    } else {
        NSLog(@"失敗");
    }

讀取:

NSString *path = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES).firstObject;
    NSString *filePath = [path stringByAppendingPathComponent:@"test.txt"];
    NSLog(@"%@",filePath);
NSArray *tmpArray = [NSArray arrayWithContentsOfFile:filePath];
    NSLog(@"%@",tmpArray);

6,數據庫
適合儲存數據量較大的數據,一般使用FMDB和CoreData來實現.

FMDB:
FMDB是iOS平臺的SQLite數據庫框架,FMDB以OC的方式封裝了SQLite的C語言API,使用起來更加面向對象,省去了很多麻煩、冗余的C語言代碼,對比蘋果自帶的Core Data框架,更加輕量級和靈活,提供了多線程安全的數據庫操作方法,有效地防止數據混亂。

CoreData:
Core Data是iOS5之後才出現的一個框架,它提供了對象-關系映射(ORM)的功能,即能夠將OC對象轉化成數據,保存在SQLite數據庫文件中,也能夠將保存在數據庫中的數據還原成OC對象。在此數據操作期間,我們不需要編寫任何SQL語句.但是直接操作CoreData顯的不是那麽容易,所以我多數的時候會使用MagicRecord來實現.MagicRecord是對CoreData的二次封裝,使用起來簡單操作方便.

FMDB 和 MagicRecord 的 demo

FMDB和MagicRecord的性能方面各有千秋,需要根據項目的實際需求進行選擇.沒有最好的方案只有最適合的方案!

FMDB與MagicRecord的性能比較

數據遷移:
如果使用到數據庫那就不得不提數據遷移的問題,不管是MagicRecord還是FMDB如果要更新數據庫都要進行數據遷移.

FMDB的數據遷移:
判斷表中有沒有這個字段,如果沒有使用sq語句插入.

/**添加字段/數據遷移*/
- (void)dataMigrationWithTableName:(NSString *)tableName newAdded:(NSString *)newAdded block:(FMDBblock)block
{
//tableName:表名
//newAdded:新加字段名
    [_queue inDatabase:^(FMDatabase * _Nonnull db) {
        if (![db columnExists:newAdded inTableWithName:tableName]){
            NSString *sql = [NSString stringWithFormat:@"ALTER TABLE %@ ADD %@ INTEGER",tableName,newAdded];
            BOOL success = [db executeUpdate:sql];
            if (success) {
                block(YES,@"字段添加成功");
            } else {
                block(NO,@"字段添加失敗");
            }
        }
    }];
}

MagicRecord的數據遷移:
首先在初始化的時候要註意初始化語句.

/**以程序名為數據庫名,不需要自動升級*/
    [MagicalRecord setupCoreDataStack];
    /**以程序名為數據庫名,需要自動升級*/
    [MagicalRecord setupAutoMigratingCoreDataStack];
    /**自定義數據庫名,不需要自動升級*/
    [MagicalRecord setupCoreDataStackWithStoreNamed:@"SQDemo.sqlite"];
    /**自定義數據庫名,需要自動升級*/
    [MagicalRecord setupCoreDataStackWithAutoMigratingSqliteStoreNamed:@"SQDemo.sqlite"];

next 新建一個模型的新版本


技術分享圖片 image.png

next 添加新字段並且選擇model2為當前模型


技術分享圖片 image.png

最後為model2新建實體類就可以了!

如果有寫的不好的地方歡迎各位指出!
參考:
https://blog.csdn.net/a416863220/article/details/48787723
https://www.jianshu.com/p/cd1a06c7ce09
http://suree.org/2015/09/29/DatabaseThink/



iOS本地數據存儲方案匯總