iOS數據持久化——屬性列表和歸檔 – iPhone手機開發 iPhone軟體開發教學課程

iOS數據持久化的方式分為三種

屬性列表 (自定義的Property List 、NSUserDefaults)

歸檔 (NSKeyedArchiver)

數據庫 (SQLite、Core Data、第三方類庫等)

本文隻介紹:屬性列表和歸檔

一. 屬性列表

Plist一般用於存儲Dictionary、Array、Boolean、Data、Date、Number、String這些類型的數據,但Boolean、Data、Date、Number、String類型的數據一般不直接存入Plist文件中,因為Plist文件有分層的概念,一般用NSDictionary或NSArray作為容器,再把其他數據類型裝入,最後把容器存入Plist文件。Plist文件不能直接存儲自定義的類,需要進行轉化成上述的數據類型,才能存儲。

(一). 數據的簡單存儲

NSDictionary或NSArray使用writeToFile:atomically:方法,會默認存儲到Plist文件

    // 獲取沙盒中documents目錄的路徑
    NSString *documentsPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)[0];

    // 拼接路徑
    NSString *someObjPath = [documentsPath stringByAppendingPathComponent:@"someObj.plist"];

    // 要寫入文件的數組
    NSArray *someObj = @[@"AAA",@"BBB",@999,@'A',@[@{@"name":@"小明"}]];

    // 把數組寫入plist文件
    // 第一個參數是文件名,第二個參數為是否使用輔助文件,如果為YES,則先寫入到一個輔助文件,然後輔助文件再重新命名為目標文件,
    // 如果為NO,則直接寫入到目標文件
    [someObj writeToFile:someObjPath atomically:YES];

(二). NSFileManager常用的文件管理

創建目錄 createDirectoryAtPath:

創建文件 createFileAtPath:

刪除某個文件 removeItemAtPath:

檢查某個文件是否存在 fileExistsAtPath:

檢查文件是否可讀 isReadableFileAtPath:

是否可寫:isWritableFileAtPath:

取得文件屬性 fileAttributesAtPath:

改變文件屬性changeAttributesAtPath:

從path代表的文件中讀取數據:contentsAtPath

移動文件movePath:toPath:handler:

復制文件copyPath:toPath:handler:

    // 1. 在沙盒裡新建目錄,再在目錄裡新建文件夾
    // 創建一個文件管理器
    NSFileManager *fileManager = [NSFileManager defaultManager];

    // 獲取沙盒下的documents路徑
    NSString *documentsPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject;

    // 拼接路徑
    NSString *newFilePath = [documentsPath stringByAppendingPathComponent:@"NewFile"];

    // 創建新目錄
    [fileManager createDirectoryAtPath:newFilePath withIntermediateDirectories:YES attributes:nil error:nil];


      // 讀寫數據一
    // 2. 向fileNew.plist寫入數據
    // 把路徑改變到新路徑下
    [fileManager changeCurrentDirectoryPath:[newFilePath stringByExpandingTildeInPath]];

    // 創建fileNew.plist文件
    [fileManager createFileAtPath:@"fileNew.plist" contents:nil attributes:nil];

    // 寫入數據
    NSArray *dataArr = @[@[@100,@"study"],@{@"name":@"xiaoming"}];

    // 拼接fileNew.plist路徑
    NSString *dataPath = [newFilePath stringByAppendingPathComponent:@"fileNew.plist"];

    // 把數組寫入fileNew.plist
    [dataArr writeToFile:dataPath atomically:YES];


    // 3. 讀取fileNew.plist數據
    NSArray *arrData = [NSArray arrayWithContentsOfFile:dataPath];

    NSLog(@"%@",arrData);



    // 讀寫數據二
    // 使用NSMutableData存儲數據要記住存儲的順序,讀取數據時要要根據那個順序計算每個數據所處的位置
    // 2. 向fileName文件寫入數據
    // 待寫入的數據
    NSString *dataStr = @"New Friend";
    int dataInt = 666;
    float dataFloat = 3.14159f;

    // 拼接存儲數據的路徑
    NSString *dataPath2 = [newFilePath stringByAppendingPathComponent:@"fileName"];

    //創建數據緩沖
    NSMutableData *writerData = [[NSMutableData alloc] init];

    //將字符串添加到緩沖中
    [writerData appendData:[dataStr dataUsingEncoding:NSUTF8StringEncoding]];

    //將其他數據添加到緩沖中
    [writerData appendBytes:&dataInt length:sizeof(dataInt)];
    [writerData appendBytes:&dataFloat length:sizeof(dataFloat)];

    //將緩沖的數據寫入到文件中
    [writerData writeToFile:dataPath2 atomically:YES];


    // 3. 讀取數據:
    // 讀取fileName文件中的數據
    NSData *readerData = [NSData dataWithContentsOfFile:dataPath2];

    // 計算dataStr在dataStr的區間,並獲取字
    dataStr = [[NSString alloc] initWithData:[readerData subdataWithRange:NSMakeRange(0, [dataStr length])]
                                   encoding:NSUTF8StringEncoding];

    // 計算dataInt在dataStr的區間,並獲取
    [readerData getBytes:&dataInt range:NSMakeRange([dataStr length], sizeof(dataInt))];

    // 計算dataFloat在dataStr的區間,並獲取
    [readerData getBytes:&dataFloat range:NSMakeRange([dataStr length] + sizeof(dataInt), sizeof(dataFloat))];

    NSLog(@"dataStr:%@   dataInt:%d   dataFloat:%f", dataStr,dataInt,dataFloat);


    // 刪除fileName文件
    [fileManager removeItemAtPath:@"fileName" error:nil];

    // 刪除一個目錄:
    // currentDirectoryPath:當前目錄
    NSLog(@"%@",fileManager.currentDirectoryPath);
    [fileManager removeItemAtPath:fileManager.currentDirectoryPath error:nil];

二. 偏好設置

(一). 知識點

偏好設置的本質還是plist文件存儲,相對於Plist文件存儲來講存儲數據更快捷,不需要獲取全路徑。

偏好設置用來保存應用程序設置和屬性、用戶保存的數據,當用戶再次打開程序或開機後這些數據仍然存在。

很多iOS應用都支持偏好設置,比如保存用戶名、密碼、字體大小等設置,iOS也提供瞭一套標準的解決方案來為應用加入偏好設置功能。 每個iOS應用都有一個NSUserDefaults實例,它是一個單例對象,通過它來存取偏好設置,設置信息都是鍵值對的形式。 用UserDefaults實例設置數據時,不是立即寫入,而是根據時間戳定時地把緩存中的數據寫入本地磁盤,所以調用瞭set方法之後數據有可能還沒有寫入磁盤,如果應用程序突然終止瞭,數據有可能寫入不成功。 偏好設置是專門用來保存應用程序的配置信息的, 一般情況不要在偏好設置中保存其他數據。 如果利用系統的偏好設置來存儲數據, 默認就是存儲在Preferences文件夾下面的,偏好設置會將所有的數據都保存到同一個文件中。 偏好設置針對同一個關鍵字對應的對象或者數據,可以對它進行重寫,重寫之後關鍵字就對應新的對象或者數據,舊的對象或者數據會被自動清理。 使用偏好設置對數據進行保存之後, 它保存到系統的時間是不確定的,會在將來某一時間點自動將數據保存到Preferences文件夾下面,如果需要即刻將數據存儲,可以使用[defaults synchronize];強制寫入偏好設置文件。 iOS7之後,如果不寫[defaults synchronize];,數據也會同步存儲。

(二). 具體使用

    NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];

    [userDefaults setValue:@"小明" forKey:@"name"];
    [userDefaults setValue:@"男" forKey:@"sex"];
    [userDefaults setValue:@"學生" forKey:@"work"];

    // 寫入磁盤
    [userDefaults synchronize];

    NSLog(@"%@",[userDefaults objectForKey:@"name"]);
    NSLog(@"%@",[userDefaults objectForKey:@"sex"]);
    NSLog(@"%@",[userDefaults objectForKey:@"work"]);

三. 歸檔與解檔

(一). 知識點

歸檔又名序列化,把對象轉為字節碼,以文件的形式存儲到磁盤上;程序運行過程中或者重新打開程序時,可以通過解歸檔(反序列化)還原這些對象。 不是所有的對象都可以直接用這種方法進行歸檔,隻有遵守瞭NSCoding協議的對象才可以,如果對象是NSString、NSDictionary、NSArray、NSData、NSNumber等類型,可以直接用NSKeyedArchiver進行歸檔。 每次歸檔對象時,都會調用encodeWithCoder:方法;一般在這個方法裡面指定如何歸檔對象中的每個實例變量,可以使用encodeObject:forKey:方法歸檔實例變量。 每次從文件中恢復(解碼)對象時,都會調用initWithCoder:方法;一般在這個方法裡面指定如何解碼文件中的數據為對象的實例變量,可以使用decodeObject:forKey方法解碼實例變量。 歸檔和解歸檔可以用於少量數據的持久化存儲和讀取,且通過歸檔創建的文件是加密的

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

(二). 具體使用

歸檔方式有以下三種:
a. 對Foundation框架中對象進行歸檔
b. 對自定義的內容進行歸檔
c. 對自定義的對象進行歸檔

1. 對Foundation框架中對象進行歸檔

    // 獲取文件路徑
    NSString *documentsPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)[0];
    // 拼接路徑
    NSString *dataPath = [documentsPath stringByAppendingPathComponent:@"NewData.data"];

    // 待歸檔的數據
    NSArray *dataArr = @[@"天天",@"編程"];

    // 歸檔
    if ([NSKeyedArchiver archiveRootObject:dataArr toFile:dataPath]) {
        NSLog(@"Archiver success !");
    }

    // 解檔
    NSArray *data = [NSKeyedUnarchiver unarchiveObjectWithFile:dataPath];

    NSLog(@"%@%@",data[0],data[1]);

2. 對自定義的內容進行歸檔

    // 獲取文件路徑
    NSString *documentsPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)[0];
    // 拼接路徑
    NSString *dataPath = [documentsPath stringByAppendingPathComponent:@"NewData.data"];

    // 創建存儲數據的NSData對象
    NSMutableData *dataM = [NSMutableData data];

    // 創建歸檔對象
    NSKeyedArchiver *archiver = [[NSKeyedArchiver alloc] initForWritingWithMutableData:dataM];

    // 添加歸檔內容
    [archiver encodeObject:@"小明" forKey:@"name"];
    [archiver encodeObject:@"男" forKey:@"sex"];
    [archiver encodeInteger:19 forKey:@"age"];

    // 完成歸檔內容
    [archiver finishEncoding];

    // 寫入磁盤
    if ([dataM writeToFile:dataPath atomically:YES]) {
        NSLog(@"Archiver success !");
    }


    // 解檔
    // 獲取數據
    NSData *data = [NSData dataWithContentsOfFile:dataPath];

    // 創建解檔對象
    NSKeyedUnarchiver *unarchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:data];

    // 解檔
    NSString *name = [unarchiver decodeObjectForKey:@"name"];
    NSString *sex = [unarchiver decodeObjectForKey:@"sex"];
    NSInteger age = [unarchiver decodeIntForKey:@"age"];


    NSLog(@"%@  %@  %ld",name,sex,age);

3. 對自定義的對象進行歸檔

---------- Person.h 文件

#import 

@interface Person : NSObject 

@property (nonatomic,strong) NSString *name;

@property (nonatomic,strong) NSString *sex;

@property (nonatomic,assign) NSInteger age;

@end

---------- Person.m 文件

#import "Person.h"

@implementation Person 

// 歸檔
- (void)encodeWithCoder:(NSCoder *)aCoder {
    [aCoder encodeObject:self.name forKey:@"name"];
    [aCoder encodeObject:self.sex forKey:@"sex"];
    [aCoder encodeInteger:self.age forKey:@"age"];
}

// 解檔
- (instancetype)initWithCoder:(NSCoder *)aDecoder {

    if (self = [super init]) {
        self.name = [aDecoder decodeObjectForKey:@"name"];
        self.sex = [aDecoder decodeObjectForKey:@"sex"];
        self.age = [aDecoder decodeIntegerForKey:@"age"];

    }
    return self;
}

@end

---------- 對Penson對象進行歸檔與解檔

    // 獲取Documents的路徑
    NSString *documentPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject;
    // 拼接路徑
    NSString *path = [documentPath stringByAppendingPathComponent:@"person.data"];

    //
    Person *per = [[Person alloc] init];
    per.name = @"xiaoming";
    per.sex = @"男";
    per.age = 20;

    // 歸檔
    [NSKeyedArchiver archiveRootObject:per toFile:path];

    // 解檔
    Person *person = [NSKeyedUnarchiver unarchiveObjectWithFile:path];

    NSLog(@"%@",person.sex);

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *