关于常见的协议-归/解档、深/浅拷贝

NSCoding

很多时候,我们需要将我们的处于内存里的对象保存到磁盘里,方便下次使用这个对象。其中,这个保存过程叫做归档-archive。相反,把磁盘里的数据变成对象,则称为解档-unarchive。

而unarchive中包含把二进制数据转化成对象的过程,称为序列化;相反,把一个对象变成磁盘里的文件这个归档过程中,对象转变成二进制数据这个过程,就就反序列化。

或者这样理解:序列化是把看似凌乱的二进制数据变成有序的结构体(内存里的对象其实就是以结构体的形式存在的),反序列化就是把有序的结构体对象变成看似凌乱的二进制数据。

NSCoding协议声明了一个可被归档与解档的类所必须实现的方法。遵循这个协议就必须实现里边的两个个方法:

// 归档
- (void)encodeWithCoder:(NSCoder *)encoder
  
// 解档
- (instancetype)initWithCoder:(NSCoder *)decoder

通常,它们是这么实现的:

// 归档
- (void)encodeWithCoder:(NSCoder *)coder {
    [coder encodeObject:self.firstName forKey:ASCPersonFirstName];// object -> 
    [coder encodeObject:self.lastName forKey:ASCPersonLastName];
    [coder encodeFloat:self.height forKey:ASCPersonHeight];// float -> 注意区别!
}

// 解档
- (id)initWithCoder:(NSCoder *)coder {
  self = [super init];
  if (self) {
      _firstName = [coder decodeObjectForKey:ASCPersonFirstName]; // -> object
      _lastName = [coder decodeObjectForKey:ASCPersonLastName];
      _height = [coder decodeFloatForKey:ASCPersonHeight]; // -> float 注意区别!
  }
  
  return self;
}

另外,需要归档 、解档的时候通常这么写:

// 1.归档
MyObject *object = [MyObject new];
NSString * documentDir= [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject];
NSString * archivePath = [documentDir stringByAppendingPathComponent:@"objFile"];

[NSKeyedArchiver archiveRootObject:object toFile:archivePath];    

// 2.解档
MyObject *object1 = [NSKeyedUnarchiver unarchiveObjectWithFile:archivePath];

NSCopying/NSMutableCopying

NSCopying

这个协议主要是用于实现自定义对象的拷贝,且是不可变的拷贝。里边也就只有一个必须实现的方法:

// 浅拷贝/指针拷贝
- (id)copyWithZone:(NSZone *)zone

你要想一个自定义的对象能得到合适的拷贝,那么你得遵循这个协议并实现这个方法,然后 自定义的类对象[aCustomInstance copy] 才可能正确工作。

ps:对象调用copy方法,其实就是简单的调用了copyWithZone:方法。

一般的实现可能是:

// 情况1-父类没有实现copyWithZone:
@interface MyBaseObject : NSObject
@property (nonatomic, copy) NSString *name;
@property (nonatomic, strong) NSDate *date;
@property (nonatomic, assign) NSInteger value;

- (id)copyWithZone:(NSZone *)zone {
    //MyObject *newObject = [MyObject new];//这种做法的问题就是:当被子类调用时,返回的对象会有问题。我们希望返回的是-目标对象。
      MyObject *newObject = [[self class new]];// 创建一个目标类的对象,通过newObject指针完成对MyObject对象的拷贝工作。
      if(newObject){
      newObject.name = _name;
        newObject.date = _date;
        newObject.value = _value;  
    }
  
    return newObject;
}

/*===========================================================================*/

// 情况2 - 父类实现了 copyWithZone:
@interface MySubObject : MyBaseObject
@property (nonatomic, copy)NSString *property1;
@property (nonatomic, copy)NSString *property2;
@end

- (id)copyWithZone:(NSZone *)zone {
  MySubObject *newObject = [super copyWithZone:zone];
  if (newObject){//完成本类中自定义属性(实例变量)的拷贝工作。
    newObject.property1 = _property1;
    newObject.property2 = _property2;
  }
  
  return newObject;
}

当MyObject类里又包含其它自定义的类,可变数组,不可变数组等情况时,则更为复杂,需要根据类设计初衷来考虑这个方法的实现。

NSMutableCopying

此协议仅声明了一个方法:mutableCopyWithZone: ,但是可变的拷贝常常是通过 mutableCopy 调起。这个mutableCopy 方法是在所有的 NSObject 类中定义,且只是简单的调用 mutableCopyWithZone: 而已。

如NSCopying协议一样,如果父类实现了NSMutableCopying协议,那么在实现子类的mutableCopyWithZone: 方法时,需先调用父类的 mutableCopyWithZone: 。

小结

感觉NSCopying协议平时在开发中使用的机会还是比较少(咳咳,其实我只是在文档、笔试题里或者技术博客里见到过一些介绍,或者常见的系统内置类型,如:NSString/NSMutableString、NSArray/NSMutableArray等)。所以具体的实战可能最好比较或者参考一下这些已有的类或者接口。

    原文作者:samingzhong
    原文地址: https://segmentfault.com/a/1190000006682302
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞