首先讲一下
copy 遵守NSCopying,并且实现 copyWithZone: 方法; 可以copy一个对象(OC已实现类,返回的是不可变对象,即使是NSMutableString和NSMutableArray,返回值 是NSString和NSArray这种)。
mutableCopy 遵守NSMutableCopying,并且实现 mutableCopyWithZone: 方法 可以mutableCopy一个对象 (NSMutableString返回是 NSMutableString)。
注意的一点是,不管是mutableCopy 还是 copy ,如果是NSArray里面的元素是对象的引用,那么复制出来的仍旧是引用。(简单的想,有一个数组A,里面的值是地址0x123,那么copy出来的数组,对应的值应该还是0x123.而0x123对应着一个对象的地址)
那怎么做深度复制,把地址0x123对应的内存也复制过去?
回想下,NSUserDefaults存放的数组,数组里面的对象的值,和 下次加载的时候对象的值是一样的,但是内存的地址是完全不一样的。
那么,这样可以做到,完全复制!
NSArray* newArr = [NSKeyedUnarchiver unarchiveObjectWithData:[NSKeyedArchiver archivedDataWithRootObject: array]];
其实就是一个序列化的过程。
接下来,如果copy 或者 序列化的容器类或者对象类里面,含有自定义的类的时候,应该怎么办?
1,copy 的问题。
CustomClass 要实现 NSCopying 协议。
- (id)copyWithZone:(NSZone *)zone;
在上面的方法中,增加初始化和复制的过程。
相当于 A = [B copy];调用的过程中,B会调用copyWithZone的函数,然后用自身的数据去分配和初始化 一个新的类,然后把这个类作为返回值Bnew, A = Bnew;
2,序列化的问题。
CustomClass 要实现 NSCoding 协议。
在- (void)encodeWithCoder:(NSCoder *)coder 函数中,把属性值一个个调用[coder encodeObject:tempValue forKey:propertyName];
在- (instancetype)initWithCoder:(NSCoder *)aDecoder 函数中,把属性值一个个[self setValue:[aDecoder decodeObjectForKey:propertyName] forKey:propertyName];
这个两步都是一个繁琐的过程。
然后就想一劳永逸下:
@interface LYCoding : NSObject <NSCoding, NSCopying>
@end
@implementation LYCoding
- (id)copyWithZone:(NSZone *)zone
{
id ret = [[[self class] allocWithZone:zone] init];
unsigned int propertyCount = 0;
objc_property_t * properties = class_copyPropertyList( [self class], &propertyCount );
for ( NSUInteger i = 0; i < propertyCount; i++ )
{
const char * name = property_getName(properties[i]);
NSString * propertyName = [NSString stringWithCString:name encoding:NSUTF8StringEncoding];
NSObject<NSCopying> * tempValue = [self valueForKey:propertyName];
if (tempValue) {
id value = [tempValue copy];
[ret setValue:value forKey:propertyName];
}
}
return ret;
}
- (void)encodeWithCoder:(NSCoder *)coder
{
unsigned int propertyCount = 0;
objc_property_t * properties = class_copyPropertyList( [self class], &propertyCount );
for ( NSUInteger i = 0; i < propertyCount; i++ )
{
const char * name = property_getName(properties[i]);
NSString * propertyName = [NSString stringWithCString:name encoding:NSUTF8StringEncoding];
NSObject * tempValue = [self valueForKey:propertyName];
// [tempValue conformsToProtocol:@protocol(NSCoding)];
[coder encodeObject:tempValue forKey:propertyName];
}
}
- (instancetype)initWithCoder:(NSCoder *)aDecoder
{
if (self = [super init]) {
unsigned int propertyCount = 0;
objc_property_t * properties = class_copyPropertyList( [self class], &propertyCount );
for ( NSUInteger i = 0; i < propertyCount; i++ )
{
const char * name = property_getName(properties[i]);
NSString * propertyName = [NSString stringWithCString:name encoding:NSUTF8StringEncoding];
[self setValue:[aDecoder decodeObjectForKey:propertyName] forKey:propertyName];
}
}
return self;
}
@end
这个是我自定义的基类,实现copy 和 coding的协议。新的类如果需要就直接继承这个类。
[tempValue conformsToProtocol:@protocol(NSCoding)];
这一行代码有什么用?
因为这个类还不完善,如果CustomA类中,有一个属性值是CustomB类。
那么需要在encode A类的时候,判断下每个值 的属性,是否实现了NSCoding,如果是,那么把它序列化成NSData.(NSData *personEncodedObject = [NSKeyedArchiver archivedDataWithRootObject:item];)
因为在这个项目,还用不到这个东西,所以就没加上去。