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保存,则文件保存内容是可见的,且无须自定义文件;否则需要自定义文件及其存储路径,当然保存的文件即使打开也是无法识别的乱码。