数组和字典都可以保存对象,如果把包含自定义对象的数组以写入到本地沙盒能保存成功么?
场景:两个按钮,分别是读写操作。数组中存放自定义对象,然后把数组存到沙盒。
自定义类
1 | @interface DBPerson : NSObject |
写数据
1 | - (IBAction)writeAction { |
输出:写入失败
因为自定义对象写入到本地沙盒需要使用归档(其实就是编码)才可以实现。
一、NSCoding
任何对象保存到本地都需要遵守NSCoding
协议,并且协议内容是必须实现的。
1 | @protocol NSCoding |
encodeWithCoder:
归档(编码),指定key值保存属性valueinitWithCoder:
解档(解码),根据key值取对应的属性value
1.1. 遵守协议
1 | @interface DBPerson : NSObject<NSCoding> |
1.2. 实现协议
1 | @implementation DBPerson |
二、归档(编码)
对象归档使用的NSKeyedArchiver
里面的archiveRootObject:
类方法。
1 | - (IBAction)writeAction { |
编译运行,崩溃了,DBPerson
中没有encodeWithCoder:
这个方法.
KeyedArchiver[79403:937363] *** Terminating app due to uncaught exception ‘NSInvalidArgumentException’, reason: ‘-[DBPerson encodeWithCoder:]: unrecognized selector sent to instance 0x6000022088c0’
原因:当执行NSKeyedArchiver
的archiveRootObject:
时,会自动调用对象的encodeWithCoder:
方法,该方法的目的就是告诉他保存对象的哪些属性。要想实现encodeWithCoder:
方法,必须遵守NSCoding
协议。
遵守协议后,我们再运行试下。输出:写入成功。
查看保存的路径:
三、解档(解码)
当执行NSKeyedUnarchiver
的unarchiveObjectWithFile:
时,会自动调用对象的initWithCoder:
方法,该方法的目的就是告诉他读取对象的哪些属性。要想实现initWithCoder:
方法,同样须遵守NSCoding
协议。
1 | - (IBAction)readAction { |
编译运行,输出结果:
1 | ( |
四、扩展
4.1. 协议不遵守也可以,本质是给对象发送encodeWithCoder:
这个消息,如果该方法没有实现才会崩溃。但为了写代码的时候能够有提示,遵守协议还是很有必要的。
4.2. 在上面的DBPerson
案例中,initWithCoder:
方法中为什么不能调用父类的initWithCoder:
方法?
1 | - (instancetype)initWithCoder:(NSCoder *)aDecoder { |
但是继承自UIView
的自定义View
就可以调用父类的initWithCoder:
方法
1 | - (instancetype)initWithCoder:(NSCoder *)aCoder { |
这是因为UIView
实现了NSCoding
协议,NSObject
没有实现该协议。只要一个类遵守了一个协议,他的子类都会遵守这个协议。
initWithCoder:
调用时机:
- 加载
IB
,开始解析对应类时调用(IB
解析完成后会调用awakeFromNib
); - 解档时会调用。