NSKeyedArchiver归档(NSCoding)

NSKeyedArchiver归档(NSCoding)

 

归档是一种很常用的文件储存方法,几乎任何类型的对象都能够被归档储存(实际上是一种文件保存的形式),特别是能够支持自定义类型对象。

 

一、实现

1、要使的需要存储的对象实现NSCoding协议,从而使他自己满足写二进制数据的能力。即是自己具有序列化的能力。

2、使用NSCoder的子类方法,实现二进制数据的动作,如读或者写。即触发存档过程。

// 属性编码 向coder中写入数据

-(void)encodeWithCoder:(NSCoder *)aCoder

{

   [aCoder encodeObject:self.nameforKey:@"name"];

   [aCoder encodeInteger:self.ageforKey:@"age"];

}

 

// 属性解码 读取coder中的数据

-(id)initWithCoder:(NSCoder *)aDecoder

{

   self = [super init];

   if (self)

   {

       self.name = [aDecoder decodeObjectForKey:@"name"];

       self.age = [aDecoderdecodeIntegerForKey:@"age"];

   }

   return self;

}

 

二、使用

1、对单个对象

(1)使用NSUserDefault进行存储等操作

/************************************************************************/


/// 保存归档对象(NSUserDefaults plist文件存储)

+ (BOOL)saveItem:(id)model key:(NSString *)key

{

    if (model && (key && 0 < key.length))

    {

        NSData *object = [NSKeyedArchiver archivedDataWithRootObject:model];


        [[NSUserDefaults standardUserDefaults] setObject:object forKey:key];

        [[NSUserDefaults standardUserDefaults] synchronize];

        

        NSLog(@"%@", NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)[0]);

        

        return YES;

    }

    

    return NO;

}


+ (void)saveItem:(id)model key:(NSString *)key complete:(void (^)(BOOL isSuccess))complete

{

    BOOL isResult = [self saveItem:model key:key];

    if (complete)

    {

        complete(isResult);

    }

}


/// 读取归档对象(NSUserDefaults plist文件存储)

+ (id)getItemWithKey:(NSString *)key

{

    if (key && 0 < key.length)

    {

        NSData *data = [[NSUserDefaults standardUserDefaults] objectForKey:key];

 

        // 解码

        id model = [NSKeyedUnarchiver unarchiveObjectWithData:data];

        return model;

    }

    

    return nil;

}


/// 删除归档对象(NSUserDefaults plist文件存储)

+ (BOOL)removeItemWithKey:(NSString *)key

{

    if (key && 0 < key.length)

    {

        [[NSUserDefaults standardUserDefaults] removeObjectForKey:key];

        [[NSUserDefaults standardUserDefaults] synchronize];

        

        return YES;

    }

    

    return NO;

}


+ (void)removeItemWithKey:(NSString *)key complete:(void (^)(BOOL isSuccess))complete

{

    BOOL isResult = [self removeItemWithKey:key];

    if (complete)

    {

        complete(isResult);

    }

}


/************************************************************************/



MAC查看路径:

打印路径:NSLog(@"%@",NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)[0]);

在Xcode5及之前,存放路径:/Users/username/Library/Application Support/iPhoneSimulator/模拟器版本/Applications/UDID/Library 的Preferences文件夹下,自己程序命名.plist。

在Xcode6及之后,路径为:/Users/username/Library/Developer/CoreSimulator/Devices/模拟器UDID/data/Library,Preferences文件夹下。

(2)使用自定义文件进行存储操作

/************************************************************************/


/// 删除归档对象(自定义文档文件存储)

+ (BOOL)removeArchivedItemWithPath:(NSString *)fileName

{

    if (fileName && 0 < fileName.length)

    {

        NSString *filePath = [self archiveFilePath:fileName];

        

        if ([[NSFileManager defaultManager] fileExistsAtPath:filePath])

        {

            BOOL isResult = [[NSFileManager defaultManager] removeItemAtPath:filePath error:nil];

            return isResult;

        }

    }

    

    return NO;

}


+ (void)removeArchivedItemWithPath:(NSString *)fileName complete:(void (^)(BOOL isSuccess))complete

{

    BOOL isResult = [self removeArchivedItemWithPath:fileName];

    if (complete)

    {

        complete(isResult);

    }

}


/// 保存归档对象(自定义文档文件存储)

+ (BOOL)saveArchivedItem:(id)model path:(NSString *)fileName

{

    if (model && (fileName && 0 < fileName.length))

    {

        NSString *filePath = [self archiveFilePath:fileName];

        

        BOOL isResult = [NSKeyedArchiver archiveRootObject:model toFile:filePath];

        return isResult;

    }

    

    return NO;

}


+ (void)saveArchivedItem:(id)model path:(NSString *)fileName complete:(void (^)(BOOL isSuccess))complete

{

    BOOL isResult = [self saveArchivedItem:model path:fileName];

    if (complete)

    {

        complete(isResult);

    }

}


/// 读取归档对象(自定义文档文件存储)

+ (id)getArchivedItemWithPath:(NSString *)fileName

{

    if (fileName && 0 < fileName.length)

    {

        NSString *filePath = [self archiveFilePath:fileName];

        

        // 解码

        id model = [NSKeyedUnarchiver unarchiveObjectWithFile:filePath];

        return model;

    }

    

    return nil;

}


// 路径

+ (NSString *)archiveFilePath:(NSString *)fileName

{

    if (!fileName || 0 == fileName.length)

    {

        return nil;

    }

    

//    // 获取根目录(即与document/library/tmp同级)

//    NSString *homeDictionary = NSHomeDirectory();

//    // 添加储存的文件名

//    NSString *archivedPath = [homeDictionary stringByAppendingPathComponent:fileName];

    

    // document目录下

    NSArray *documentArray = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);

    NSString *document = [documentArray objectAtIndex:0];

    NSString *archivedPath = [document stringByAppendingPathComponent:fileName];

    

    NSLog(@"filePath %@", archivedPath);

    

    return archivedPath;

}


/************************************************************************/


2、对多个对象的归档

/************************************************************************/


/// 多个对象归档

+ (BOOL)saveArchivedItems:(NSDictionary *)dict path:(NSString *)fileName

{

    if ((dict && 0 != dict.count) && (fileName && 0 != fileName.length))

    {

        NSString *filePath = [self archiveFilePath:fileName];

      

        NSMutableData *data = [[NSMutableData alloc] init];

        NSKeyedArchiver *archvier = [[NSKeyedArchiver alloc] initForWritingWithMutableData:data];

        for (NSString *key in dict.allKeys)

        {

            id value = dict[key];

            [archvier encodeObject:value forKey:key];

        }

        [archvier finishEncoding];

        BOOL result = [data writeToFile:filePath atomically:YES];

        return result;

    }

    

    return NO;

}


+ (void)saveArchivedItems:(NSDictionary *)dict path:(NSString *)fileName complete:(void (^)(BOOL isSuccess))complete

{

    BOOL result = [self saveArchivedItems:dict path:fileName];

    if (complete)

    {

        complete(result);

    }

}


/// 获取某个归档对象

+ (id)getArchivedItemWithKey:(NSString *)key path:(NSString *)fileName

{

    if ((key && 0 != key.length) && (fileName && 0 != fileName.length))

    {

        NSString *filePath = [self archiveFilePath:fileName];

        

        NSMutableData *data = [[NSMutableData alloc] initWithContentsOfFile:filePath];

        NSKeyedUnarchiver *unarchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:data];

        id value = [unarchiver decodeObjectForKey:key];

        [unarchiver finishDecoding];

        

        return value;

    }

    

    return nil;

}


/************************************************************************/


三、特别推荐

因为归档必须实现NSCoding协议,且重写方法时,每个属性都应该设置编解码,这样操作起来是比较烦琐的。所以我们可以创建一个基类,在基类里实现NSCoding协议,同时使用runtime进行时设置属性的编解码,而后其他自定义数据类型对象均继承这个基类,则不必再重写烦琐的协议方法了。

.h文件

#import <Foundation/Foundation.h>


@interface BaseArchivedObject : NSObject


@end


.m文件

#import "BaseArchivedObject.h"

#import <objc/runtime.h>


@implementation BaseArchivedObject


// 解档

- (id)initWithCoder:(NSCoder *)decoder

{

    self = [super init];

    if (self)

    {

        unsigned int count = 0;

        // 获取类中所有成员变量名

        Ivar *ivar = class_copyIvarList([self class], &count);

        for (int i = 0; i < count; i++)

        {

            Ivar iva = ivar[i];

            const char *name = ivar_getName(iva);

            NSString *strName = [NSString stringWithUTF8String:name];

            // 进行解档取值

            id value = [decoder decodeObjectForKey:strName];

            // 利用KVC对属性赋值

            [self setValue:value forKey:strName];

        }

        free(ivar);

    }

    return self;

}


// 归档

- (void)encodeWithCoder:(NSCoder *)encoder

{

    unsigned int count;

    Ivar *ivar = class_copyIvarList([self class], &count);

    for (int i = 0; i < count; i++)

    {

        Ivar iv = ivar[i];

        const char *name = ivar_getName(iv);

        NSString *strName = [NSString stringWithUTF8String:name];

        

        // 利用KVC取值

        id value = [self valueForKey:strName];

        [encoder encodeObject:value forKey:strName];

    }

    

    free(ivar);

}


@end




注意:

1、通过plist保存的数据是直接显示的,不安全。通过归档方法保存的数据在文件中打开是乱码的,更安全。

2、存储时,如果是通过NSUserDefault保存,则文件保存内容是可见的,且无须自定义文件;否则需要自定义文件及其存储路径,当然保存的文件即使打开也是无法识别的乱码。

你可能感兴趣的:(NSKeyedArchiver归档(NSCoding))