NSCoding协议

NSCoding 是一个简单的协议,只有有两个方法:-initWithCoder:encodeWithCoder:。遵循NSCoding协议的类可以被序列化(编码)和反序列化(解码),这样可以归档到磁盘上或分发到网络上。

自定义的类 只需要遵循NSCoding 协议并实现其两个方法就能使用NSKeyedArchiverNSKeyedUnarchiver就行归档解档

自定义Book类遵循NSCoding协议:

@interface Book : NSObject <NSCoding>
@property (nonatomic , copy)  NSString *title;
@property (nonatomic , copy)  NSString *author;
@property (nonatomic , assign) NSUInteger pageCount;
@property (nonatomic , strong)  NSSet *categories;
@property (nonatomic , assign,getter = isAvailable) BOOL available;
@end

@implementation Book

#pragma mark - NSCoding

- (id)initWithCoder:(NSCoder *)decoder {
    self = [super init];
    if (!self) {
        return nil;
    }

    self.title = [decoder decodeObjectForKey:@title];
    self.author = [decoder decodeObjectForKey:@author];
    self.pageCount = [decoder decodeIntegerForKey:@pageCount];
    self.categories = [decoder decodeObjectForKey:@categories];
    self.available = [decoder decodeBoolForKey:@available];

    return self;
}

- (void)encodeWithCoder:(NSCoder *)encoder {
    [encoder encodeObject:self.title forKey:@title];
    [encoder encodeObject:self.author forKey:@author];
    [encoder encodeInteger:self.pageCount forKey:@pageCount];
    [encoder encodeObject:self.categories forKey:@categories];
    [encoder encodeBool:[self isAvailable] forKey:@available];
}

@end

使用NSKeyedArchiverNSKeyedUnarchiver就行归档解档

-(IBAction)save {  
1.新的模型对象
   Book*book=[[Bookalloc]init];
   book.title=@狂人日记;
   book.author=@鲁迅;
   book.pageCount = 30;
   book.categories = [[NSSet alloc] initWithObjects:@1, @2, @3,  nil ];
   book.available = YES;

//2.归档模型对象
//2.1.获得Documents的全路径
    NSString *doc=[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask,YES) lastObject];

//2.2.获得文件的全路径
    NSString*path=[docstringByAppendingPathComponent:@book.data];

//2.3.将对象归档 存储到沙盒
    [NSKeyedArchiverarchiveRootObject:booktoFile:path];
}
- (IBAction)read {
//1.获得Documents的全路径
   NSString*doc=[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask,YES) lastObject];

//2.获得文件的全路径
   NSString*path=[doc stringByAppendingPathComponent:@book.data];

//3.从文件中读取book对象
   Book*book= [NSKeyedUnarchiver unarchiveObjectWithFile:path];
}

扩展①:NSUserDefaults支持的数据类型【都是不可变的】有:NSNumber(NSInteger、float、double),NSString,NSDate,NSData,NSArray,NSDictionary,BOOL. 如果要存储自定义类型 需要转成NSData

   Book*book=[[Bookalloc] init];
   book.title = @呐喊;
   book.author = @鲁迅;
   book.pageCount = 60;
   book.categories = [[NSSet alloc] initWithObjects:@1, @2, @3, 
   NSData *data = [[NSKeyedArchiver archivedDataWithRootObject:book]]
//先转成NSData 再存储
   NSData *data = [NSKeyedArchiver archivedDataWithRootObject:book];
   [[NSUserDefaults standardUserDefaults] setObject:data forKey:@book];
//取数据
   NSData *myData = [[NSUserDefaults standardUserDefaults] objectForKey:@book];
   Book *myBook = [NSKeyedUnarchiver unarchiveObjectWithData:myData]

扩展②:利用runtime更加高效的进行归档解决档

///归档
- (void)encodeWithCoder:(NSCoder *)aCoder {
    unsigned int count;
    // 获得指向当前类的所有属性的指针
    objc_property_t *properties = class_copyPropertyList([self class], &count);
    for (int i = 0; i < count; i++) {
        // 获取指向当前类的一个属性的指针
        objc_property_t property = properties[i];
        const char *name = property_getName(property);
        NSString *propertyName = [NSString stringWithUTF8String:name];
        NSString *propertyValue = [self valueForKeyPath:propertyName];
        // 编码属性
        [aCoder encodeObject:propertyValue forKey:propertyName];
    }
    free(properties);
}
///解档
- (id)initWithCoder:(NSCoder *)aDecoder  {
    if(self = [super init])
    {
        unsigned int count;
        // 获得指向当前类的所有属性的指针
        objc_property_t *properties = class_copyPropertyList([self class], &count);
        for (int i = 0; i < count; i++) {
            objc_property_t property = properties[i];
            const char *name = property_getName(property);
            NSString *propertyName = [NSString stringWithUTF8String:name];
    //解码属性
            NSString *propertyValue = [aDecoder decodeObjectForKey:propertyName];
            [self setValue:propertyValue forKey:propertyName];
        }
        free(properties);
    }
    return self;
}