#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
// 归档的前提:被归档的对象需要遵守编码协议,并实现相应方法
@interface User : NSObject <NSCoding>
@property (nonatomic,assign) NSInteger age; // 年龄integer类型
@property (nonatomic,copy) NSString *name; // 姓名,oc类型
@property (nonatomic,strong) UIImage *image; // 图片,UIImage类型
@end
#import "User.h"
#import <objc/runtime.h> // 导入运行时头文件
// 注意,不把存储我们 属性名 和 属性类型字典 进行归档
// 因为归档是一个重新创建的过程,因为我们把 存储 属性名 和 属性类型的字典设置为全局的
static NSMutableDictionary *proDic = nil;
@interface User () <NSCacheDelegate>
@end
@implementation User
// 归档是一个编码的过程
- (void)encodeWithCoder:(NSCoder *)aCoder
{
proDic = [NSMutableDictionary dictionary];
// 获取类中的实例变量
unsigned int count; // 属性个数
// 定义Ivar, 获取所有的实例变量
Ivar *varArray = class_copyIvarList([self class], &count);
// 循环归档的过程
for (int i = 0; i < count; i++) {
Ivar var = varArray[i];
// 1. 获取属性名称 : age name image
const char *cName = ivar_getName(var); // 获取属性名,c字符串
NSString *proName = [[NSString stringWithUTF8String:cName] substringFromIndex:1]; // OC字符串
// 注意需要截取一下
// 2. 获取属性类型 : NSInteger NSString UIImage
const char *cType = ivar_getTypeEncoding(var); // 获取 变量类型。
// 3. kvc 取值
id value = [self valueForKey:proName];
// 4. 把属性类型 C 字符串 转化为 OC 字符串,会加上 @" "
// 属性类型
NSString *proType = [NSString stringWithUTF8String:cType];
// 5. 如果是OC类型数据,且不属于 NSNumber类,把 @" " 去掉
if ([value isKindOfClass:[NSObject class]] && ![value isKindOfClass:[NSNumber class]]) {
// 截取前: @"@\"NSString\""
// 截取后: @\"NSString\"
NSUInteger length = proType.length;
proType = [proType substringWithRange:NSMakeRange(2, length - 3)];
}
// 6. 给 存储属性 和 属性类型的 字典赋值 属性名(key)___ 属性类型(value)
if (![proName isEqualToString:@"proDic"]) {
[proDic setValue:proType forKey:proName];
}
// 注意: 这里c语言基本数据类型,可以写一个数组,然后判断是否在数组
// 注意,不把存储我们 属性名 和 属性类型字典 进行归档
if ([proType isEqualToString:@"q"]) { // 如果属性类型为q, 说明为NSInteger类型为age
// 说明是 NSInteger类型,使用KVC取值,取出的是 NSNumber类型,需要转化为integer类型
[aCoder encodeInteger:[value integerValue] forKey:proName];
} else if ([proType isEqualToString:@"UIImage"]) // image
{
// 说明是图片,需要转化为data再存储
[aCoder encodeDataObject:UIImagePNGRepresentation(value)];
}else if ([value isKindOfClass:[NSObject class]] && ![proName isEqualToString:@"proDic"]) // string
{
// 如果 value 是oc对象,使用Object归档
[aCoder encodeObject:value forKey:proName];
}
} // for 循环归档结束
}
// 反归档是一个解码的过程,初始化方法重新生成对象
- (nullable instancetype)initWithCoder:(NSCoder *)aDecoder
{
self = [super init];
if (self) {
// 1. 遍历变量名, 变量名为 归档、返归档 标识
for (NSString *name in proDic) {
// 2. 获取变量类型,根据类型反归档
NSString *type = proDic[name];
// 循环反归档过程
// kvc 赋值
if ([type isEqualToString:@"q"]) { // age
NSInteger integer = [aDecoder decodeIntegerForKey:name]; // 注意需要把 NSNumber ----> NSInteger 类型
// 优化部分:需要赋值的对象 这里需要用到 setObjectForKey NSCache 以后解决
Ivar var = class_getClassVariable([self class], name.UTF8String);
// obje
self.age = integer;
} else if ([type isEqualToString:@"UIImage"]) {
UIImage *image = [UIImage imageWithData:[aDecoder decodeDataObject]];
[self setValue:image forKey:name];
} else
{
id value = [aDecoder decodeObjectForKey:name];
[self setValue:value forKey:name];
}
}
}
return self;
}
#pragma mark-
#pragma mark NSCache 协议方法
//- (void)cache:(NSCache *)cache willEvictObject:(id)obj
//{
//
//}
@end
#import "ViewController.h"
#import <objc/runtime.h>
#import "User.h"
#define varString(var) [NSString stringWithFormat:@"%s",#var]
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// objec/runtime
// OC 动态运行时 之 归档反归档
unsigned int count;
// 获取类的 实例变量
Ivar *array = class_copyIvarList([Person class], &count);
for (int i = 0; i < count; i++) {
// 获取 实例变量的类型
NSLog(@"%s", ivar_getTypeEncoding(array[i]));
}
/*
2015-11-22 14:27:32.647 runtime之归档反归档[59048:1932097] q
2015-11-22 14:27:32.647 runtime之归档反归档[59048:1932097] @"NSString"
2015-11-22 14:27:32.648 runtime之归档反归档[59048:1932097] @"UIImage"
typedef long NSInteger ivar_getTypeEncoding q
*/
User *user = [[User alloc] init];
user.age = 12;
// [person setValue:@12 forKey:@"age"];
NSInteger age = [[user valueForKey:@"age"] integerValue];
// kvc 赋值,虽然可以会把 基本数据类型 ,转化为 NSNumber 添加进去,
// 但是取值的时候一定要注意,也需要把 NSNumber 转化为 interger,否则打印的integer 值不正确
NSLog(@"%ld", age);
NSLog(@"%@", [person valueForKey:@"age"]);
User *p1 = [[Person alloc] init];
p1.age = 16;
p1.name = @"XL";
p1.image = [UIImage imageNamed:@"avatar.jpg"];
// 归档
NSMutableData *data1 = [NSMutableData data];
NSKeyedArchiver *archiver = [[NSKeyedArchiver alloc] initForWritingWithMutableData:data1];
NSString *varName = varString(p1);
[archiver encodeObject:p1 forKey:varName];
[archiver finishEncoding];
// NSLog(@"%@", data1);
NSLog(@"%@", varName);
// 反归档
NSKeyedUnarchiver *unArchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:data1];
User *p2 = [unArchiver decodeObjectForKey:varName];
[unArchiver finishDecoding];
NSLog(@"%@", p2);
UIImageView *imageView = [[UIImageView alloc] initWithFrame:(CGRectMake(100, 100, 200, 200))];
imageView.image = p2.image;
[self.view addSubview:imageView];
NSLog(@"%ld", p2.age);
NSLog(@"%@", p2.name);
// Do any additional setup after loading the view, typically from a nib.
}
@end