iOS數據持久化存儲:歸檔


在平時的iOS開發中,我們經常用到的數據持久化存儲方式大概主要有:NSUserDefaults(plist),文件,數據庫,歸檔。。前三種比較經常用到,第四種歸檔我個人感覺用的還是比較少的,恰恰因為用的比較少,但是還是有地方要用到,所以今天再把歸檔解檔復習一遍吧。

一、什么是歸檔:

  對象歸檔是將對象以文件的形式保存到磁盤中(也稱為序列化,持久化) ;使用的時候讀取該文件的保存路徑讀取文件的內容(也稱為解檔,反序列化)

二、歸檔 與 plist存儲的區別:

  • 對象歸檔的文件是保密的,在磁盤上無法查看文件中的內容,而plist屬性列表是明文的可以查看。
  • 其次就是保存的數據類型不同
    • 只要是對象類型,歸檔都可以保存。
    • 而plist只能保存這幾種類型:NSString  NSNumber  NSDate NSData NSArray NSDictionary  。不能保存其他的類型

     

三、歸檔的使用

  剛剛說到,只要是對象類型,都可以用歸檔的方式進行保存。

  但是,只有實現了<NSCoding>協議的類型才可以進行歸檔,由於Foudation框架中的對象類型已經實現了NSCoding協議,所以可以直接歸檔解檔,而我們自定義的類型,則需要我們手動去實現NSCoding協議。必須實現一下兩個方法:

//歸檔的時候調用的方法
- (void)encodeWithCoder:(NSCoder *)aCoder;
//解歸檔的時候要調用的函數
- (id)initWithCoder:(NSCoder *)aDecoder;

  1.對系統類的歸檔:

    第一種方式:單個對象的歸檔和解檔

//單個對象歸檔:
    NSArray *array1 = @[@"zhangsan",@"wangwu",@"lisi"];
    NSString *filePath = [NSHomeDirectory() stringByAppendingPathComponent:@"array.src"];
    BOOL success = [NSKeyedArchiver archiveRootObject:array1 toFile:filePath];
    if (success) {
        NSLog(@"保存成功!");
    }
    //單個對象解檔
    id array2 =  [NSKeyedUnarchiver unarchiveObjectWithFile:filePath];
    NSLog(@"%@",array2);

    第二種方式:多個對象的歸檔和解檔

    //多個對象歸檔:
    NSArray *array1 = @[@"zhangsan",@"wangwu",@"lisi"];
    NSMutableData *data = [NSMutableData data];
    NSKeyedArchiver *archiver = [[NSKeyedArchiver alloc] initForWritingWithMutableData:data];
    //編碼
    [archiver encodeObject:array1 forKey:@"array"];
    [archiver encodeInt:100 forKey:@"scope"];
    [archiver encodeObject:@"jack" forKey:@"name"];
    //完成編碼,講上面的歸檔數據填充到data中,此時data已經存儲了歸檔對象的數據
    [archiver finishEncoding];
    NSString *filePath = [NSHomeDirectory() stringByAppendingPathComponent:@"array.src"];
    BOOL success = [data writeToFile:filePath atomically:YES];
    if (success) {
        NSLog(@"歸檔成功");
    }
    
    //多個對象解檔
    NSData *data2 = [[NSData alloc] initWithContentsOfFile:filePath];
    NSKeyedUnarchiver *unarchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:data2];
    NSArray *array = [unarchiver decodeObjectForKey:@"array"];
    NSLog(@"%@",array);
    int value = [unarchiver decodeIntForKey:@"scope"];
    NSLog(@"%d",value);
  [unarchiver finishDecoding];


  2.自定義類型的歸檔解檔

  上面已經說過了,對系統類型可以直接進行歸檔和解檔,但是對於自定義類型,必須實現NSCoding協議

#import <Foundation/Foundation.h>
#import "Car.h"

int main(int argc, const char * argv[])
{
    /*
     有輛汽車 有一個引擎 和 四個輪胎
     
     然后需要對這個汽車進行歸檔 本地保存數據
     這時 還必須要對汽車的引擎和輪胎也要歸檔
     
     只要有成員變量是對象地址 都要繼續歸檔 
     
     那么這時歸檔的類 都要遵守協議NSCoding 實現里面的方法
     
     實際上 歸檔就是一個深拷貝
     */
    
    @autoreleasepool {
     
#if 0
        Car *BMW = [[Car alloc] init];
        BMW.speed = 100;
        [BMW run];
        
        //程序結束之前把汽車對象的信息進行保存到本地
        //歸檔
        
        NSString *filePath = [NSHomeDirectory() stringByAppendingPathComponent:@"car.src"];
        
        BOOL ret = [NSKeyedArchiver archiveRootObject:BMW toFile:filePath];
        if (ret) {
            NSLog(@"汽車對象歸檔成功");
        }else {
            NSLog(@"汽車歸檔失敗");
        }
        [BMW release];
        
#else
        //解歸檔-->從文件中獲取數據創建一個Car對象
        
        NSString *filePath = [NSHomeDirectory() stringByAppendingPathComponent:@"car.src"];

        Car *car = [NSKeyedUnarchiver unarchiveObjectWithFile:filePath];
        
        NSLog(@"car.speed:%ld",car.speed);
        NSLog(@"engine.name:%@",car.engine.name);
        [car run];
    
#endif
        
    }
    return 0;
}

  汽車類:

#import <Foundation/Foundation.h>
#import "Engine.h"
#import "Tyre.h"

//遵守歸檔協議
@interface Car : NSObject<NSCoding>
{
    Engine *_engine;
    NSMutableArray *_tyresArr;
    NSInteger _speed;
}
@property (nonatomic) NSInteger speed;
@property (nonatomic,retain) Engine *engine;
- (Car *)init;
- (void)run;
@end
#import "Car.h"

@implementation Car
- (void)dealloc {
    NSLog(@"汽車銷毀");
    [_engine release];
    [_tyresArr release];
    [super dealloc];
}

//對Car歸檔的時候會調用的方法
- (void)encodeWithCoder:(NSCoder *)aCoder {
    //就是對成員變量的數據進行歸檔
    //如果成員變量是基本類型 那么直接歸檔
    //如果成員變量變量是對象地址 那么對象的類也要遵守協議實現方法
    
    //在類中 如果有setter和getter方法那么盡量使用setter和getter方法
    [aCoder encodeInteger:self.speed forKey:@"speed"];
    //對一個對象地址指向的對象進行歸檔 那么指向對象也要實現歸檔協議的方法 Engine 要遵守 數組要遵守 數組的元素也要遵守
    [aCoder encodeObject:self.engine forKey:@"engine"];
    [aCoder encodeObject:_tyresArr forKey:@"array"];
}
//對象解歸檔、讀檔的時候調用
- (id)initWithCoder:(NSCoder *)aDecoder {
    if (self = [super init]) {//如果父類有initWithCoder那么調用父類的initWithCoder:
        
        //這里不要直接寫 成員變量 否則就有可能崩潰
        //要用setter方法 這樣 內部會計數器+1 擁有絕對使用權
        //讀檔
        self.engine = [aDecoder decodeObjectForKey:@"engine"];
        NSLog(@"%ld",self.engine.retainCount);
        self.speed = [aDecoder decodeIntegerForKey:@"speed"];
        //要注意擁有絕對使用權
        _tyresArr = [[aDecoder decodeObjectForKey:@"array"] retain];
        
    }
    return self;
}



- (Car *)init {
    if (self = [super init]) {
        _engine = [[Engine alloc] init];
        _engine.name = @"德國";
        _engine.power = 1000;
        
        _tyresArr = [[NSMutableArray alloc] init];
        for (int i = 0; i < 4; i++) {
            Tyre *tyre = [[Tyre alloc] init];
            tyre.name = @"米其林";
            tyre.type = 12345;
            [_tyresArr addObject:tyre];
            [tyre release];
        }
    }
    return self;
}
- (void)run {
    NSLog(@"汽車跑");
    [self.engine start];
    for (Tyre *tyre in _tyresArr) {
        [tyre scroll];//滾動
    }
}

@end

輪胎類:

#import <Foundation/Foundation.h>

@interface Tyre : NSObject<NSCoding>
{
    NSString *_name;
    NSInteger _type;
}
@property (nonatomic,copy)NSString *name;
@property (nonatomic)NSInteger type;
- (void)scroll;

@end
#import "Tyre.h"

@implementation Tyre
- (void)dealloc {
    NSLog(@"輪胎銷毀");
    self.name = nil;
    [super dealloc];
}
//歸檔調用
- (void)encodeWithCoder:(NSCoder *)aCoder {
    [aCoder encodeObject:self.name forKey:@"name"];
    [aCoder encodeInteger:self.type forKey:@"type"];
}
//讀檔調用
- (id)initWithCoder:(NSCoder *)aDecoder {
    if (self = [super init]) {
        //用setter方賦值 內部有計數器+1 
        self.name = [aDecoder decodeObjectForKey:@"name"];
        self.type = [aDecoder decodeIntegerForKey: @"type"];
    }
    return self;
}


- (void)scroll {
    NSLog(@"%@ 輪胎在滾動",self.name);
}
@end

引擎類:

#import <Foundation/Foundation.h>

@interface Engine : NSObject <NSCoding>
{
    NSString *_name;
    NSInteger _power;
}
@property (nonatomic,copy)NSString *name;
@property (nonatomic) NSInteger power;
- (void)start;
@end
#import "Engine.h"

@implementation Engine
- (void)dealloc {
    NSLog(@"引擎銷毀");
    self.name = nil;
    [super dealloc];
}
//歸檔調用
- (void)encodeWithCoder:(NSCoder *)aCoder {
    [aCoder encodeObject:self.name forKey:@"name"];
    [aCoder encodeInteger:self.power forKey:@"power"];
}
//讀檔調用
- (id)initWithCoder:(NSCoder *)aDecoder {
    if (self = [super init]) {
        self.name = [aDecoder decodeObjectForKey:@"name"];
        self.power = [aDecoder decodeIntegerForKey: @"power"];
    }
    return self;
}

- (void)start {
    NSLog(@"引擎啟動");
}
@end

 

 
 

注意!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。



 
粤ICP备14056181号  © 2014-2021 ITdaan.com