NSCoding:自定義類的對象歸檔和序列化

序言:最近項目開發(fā)有一需求:本公司開發(fā)的是智能控制系統(tǒng),每臺機器初始設定時,需要用戶設定各個參數(shù)的值,由于頁面參數(shù)眾多,用戶對一臺設定好參數(shù)后,可能需要對其他的機器也所有參數(shù)設定同樣的值。此時需求就出來:每當用戶設定好參數(shù)后,點擊頁面的保存按鈕后,就會保存該參數(shù)模板到本地。當對其他機器設定是,頁面會出現(xiàn)使用上次設置按鈕,當用戶點擊此按鈕時,就會將本地保存的所有有效的參數(shù)值按順序進行下發(fā)設定(有效值:每個參數(shù)都會有默認值,一般默認值都是該類型的最大值,沒有意義)。


分析:


初期,我的開發(fā)思路是這樣的:

  • 1. 對應每個頁面建一個model,然后在model中按頁面參數(shù)順序定義所對應的參數(shù)值assign類型的存儲屬性。初始化時會對所有的參數(shù)值初始化一個默認值,即最大值;
  • 2. 當用戶點擊保存時,會取出當前頁面所有的參數(shù)值,會按順序?qū)⒂行У膮?shù)值寫入model的存儲屬性中(默認值就不寫入);
  • 3. 當用戶點擊使用上次設置時,會取出該該model的屬性值,按順序下發(fā)有效的參數(shù)值。

弊端:對應每個頁面都需要創(chuàng)建一個model,對應多個頁面就需要多個model;然后每個Model需要定義多個存儲屬性以一一對應頁面的參數(shù)。最后可能需要第三方的框架(MJExtension),將每個模型對象轉(zhuǎn)換成NSString對象,繼而將該對象存儲到沙盒。還要從沙盒中去取該NSString對象,然后轉(zhuǎn)成model對象。
model的.m文件中

- (void)save {
    //將初始化模型(InitiatorModel)轉(zhuǎn)為json字符串
    NSString *json = [self mj_JSONString];
    //寫入用戶偏好設置數(shù)據(jù)庫
    [[NSUserDefaults standardUserDefaults] setObject:json forKey:ACTIVE_REACTIVE_MODEL_KEY];
}

+ (instancetype)activeReactiveModel {
    //從用戶偏好設置數(shù)據(jù)庫取json字符串
    NSString *json = [[NSUserDefaults standardUserDefaults] objectForKey:ACTIVE_REACTIVE_MODEL_KEY];
    if (!json) {
        return nil;
    }
    //通過json字符串轉(zhuǎn)為初始化模型(InitialModel)
    ActiveReactiveModel *activeReactiveModel = [ActiveReactiveModel mj_objectWithKeyValues:json];
    return activeReactiveModel;
}

開發(fā)完成之后,基本功能都可以實現(xiàn),但是發(fā)現(xiàn)擴展性不好,每個頁面都對應一個model。后來和同事溝通交流,一同事說有更好的實現(xiàn)方式,利用NSKeyedArchiver實現(xiàn)對象的歸檔。但是有個前提,一般對象都是系統(tǒng)Foundation框架下的類的對象都可以實現(xiàn)。究其根由,Foundation框架下的類都是遵從NSCoding協(xié)議??聪绿O果開發(fā)文檔。

NSCoding.png

所有在自己定義的模型model中遵從NSCoding協(xié)議,然后實現(xiàn)該協(xié)議的兩個協(xié)議方法。
model文件SARegister
SARegister.h文件:

#import <Foundation/Foundation.h>

@interface SARegister : NSObject<NSCoding>

/// 寄存器地址
@property (nonatomic, assign) NSUInteger address;
/// 寄存器個數(shù)(從文件中讀取時確定)
@property (nonatomic, assign, readonly) NSUInteger length;
/// 讀取上來的 rawValue
@property (nonatomic, strong) NSData *valueData;

/// 計算屬性
@property (nonatomic, assign) NSInteger value;
@property (nonatomic, strong) NSString *utf8Value;

/// 最近一次跟新 value 的時間
@property (nonatomic, strong) NSDate *lastDate;
@property (nonatomic, strong) NSString *name;
/** 通過registerId來獲取唯一的寄存器 */
@property (nonatomic, strong) NSString *registerId;

@end

SARegister.m文件:

#import "SARegister.h"

@implementation SARegister
...
//省略代碼
...
#pragma mark - NSCoding
- (instancetype)initWithCoder:(NSCoder *)aDecoder {
    self = [super init];
    if (self) {
        self.address = [aDecoder decodeIntegerForKey:@"address"];
        self.valueData = [aDecoder decodeObjectForKey:@"valueData"];
        self.lastDate = [aDecoder decodeObjectForKey:@"lastDate"];
        self.name = [aDecoder decodeObjectForKey:@"name"];
        self.registerId = [aDecoder decodeObjectForKey:@"registerId"];
    }
    return self;
}

- (void)encodeWithCoder:(NSCoder *)aCoder {
    [aCoder encodeInteger:self.address forKey:@"address"];
    [aCoder encodeObject:self.valueData forKey:@"valueData"];
    [aCoder encodeObject:self.lastDate forKey:@"lastDate"];
    [aCoder encodeObject:self.name forKey:@"name"];
    [aCoder encodeObject:self.registerId forKey:@"registerId"];
}

@end

頁面的ViewController.m文件中:具體展示保存和取出model數(shù)組

//文件保存路徑:Library/Application Support
- (NSString *)archivePath {
    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, YES);
    NSString *documentsPath = @"";
    if (paths.count > 0) {
        documentsPath = paths.firstObject;
        if (! [[NSFileManager defaultManager] fileExistsAtPath:documentsPath]) {
            NSError *error;
            if(![[NSFileManager defaultManager] createDirectoryAtPath:documentsPath withIntermediateDirectories:YES attributes:nil error:&error]) {
                NSLog(@"Failed to create directory. error: %@",error);
            }
        }
    }
    return documentsPath;
}

//保存參數(shù)模板(model數(shù)組)
- (void)saveModel{
    [[NSFileManager defaultManager] changeCurrentDirectoryPath:self.archivePath];
    BOOL succes = [NSKeyedArchiver archiveRootObject:self.saRegisters toFile:self.titleName];
        if (succes) {
            [Utility showSuccessToastStatus:NSLocalizedString(@"保存成功", @"保存成功")];
        } else {
            [Utility showErrorToastStatus:NSLocalizedString(@"數(shù)據(jù)同步失敗"", @"數(shù)據(jù)同步失敗")];
        }
}

//取出model數(shù)組
- (NSArray<SAMenuItem *>*)useTemplate {
    if (self.canSave) {
        NSString *path = [self.archivePath stringByAppendingPathComponent:self.titleName];
        NSArray<SAMenuItem *> *archiver = [NSKeyedUnarchiver unarchiveObjectWithFile:path];
        return archiver;
    }
    return nil;
}

注意:如果定義的model中的某個屬性同樣也是自己定義的一個類的對象,那么該類也是要遵從NSCoding協(xié)議,然后實現(xiàn)兩個協(xié)議方法。

- (instancetype)initWithCoder:(NSCoder *)aDecoder;
- (void)encodeWithCoder:(NSCoder *)aCoder;
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容