自定义模型拷贝copy

208 阅读2分钟

自定义model拷贝或者数组copy时,对元素内容深拷贝,有以下几种方式:

  • 方法一:最原始的办法,new一个新model逐个赋值
  • 方法二:使用系统- (instancetype)initWithArray:(NSArray<ObjectType> *)array copyItems:(BOOL)flag方法,并实现model的NSCopying,NSMutableCopying协议
    • 如果model里有子model,子model也需要实现NSCopying,NSMutableCopying协议
  • 方法三:使用归档解档 NSCoding协议,重写- (void)encodeWithCoder:(NSCoder *)encoder- (id)initWithCoder:(NSCoder *)decoder
  • 方法四:NSUserDefaults存储读取自定义对象
  • 方法五:利用YYModel、MTLModel等三方模型转换

下面具体说下除方法一之外的大致用法。 示例模型如下:

//Person.h
@interface Person
@property (nonatomic) NSString *name;
@property (nonatomic, assign) NSInteger code;
@end

方法二:使用系统- (instancetype)initWithArray:(NSArray<ObjectType> *)array copyItems:(BOOL)flag方法,并实现model的NSCopying,NSMutableCopying协议

如果model里有子model,子model也需要实现NSCopying,NSMutableCopying协议

#import <objc/message.h>

@interface Person ()<NSCopying,NSMutableCopying>
@end

@implementation Person

- (id)copyWithZone:(NSZone *)zone {
    id obj = [[[self class] allocWithZone:zone] init];
    Class class = [self class];
    while (class != [NSObject class]) {
        unsigned int count;
        Ivar *ivar = class_copyIvarList(class, &count);
        for (int i = 0; i < count; i++) {
            Ivar iv = ivar[i];
            const char *name = ivar_getName(iv);
            NSString *strName = [NSString stringWithUTF8String:name];
            //利用KVC取值
            id value = [[self valueForKey:strName] copy];//如果还套了模型也要copy
            [obj setValue:value forKey:strName];
        }
        free(ivar);
        
        class = class_getSuperclass(class);//遍历父类属性
    }
    return obj;
}

-(id)mutableCopyWithZone:(NSZone *)zone{
    id obj = [[[self class] allocWithZone:zone] init];
    Class class = [self class];
    while (class != [NSObject class]) {
        unsigned int count;
        Ivar *ivar = class_copyIvarList(class, &count);
        for (int i = 0; i < count; i++) {
            Ivar iv = ivar[i];
            const char *name = ivar_getName(iv);
            NSString *strName = [NSString stringWithUTF8String:name];
            //利用KVC取值
            id value = [[self valueForKey:strName] copy];//如果还套了模型也要copy
            [obj setValue:value forKey:strName];
        }
        free(ivar);
        
        class = class_getSuperclass(class);//遍历父类的属性
    }
    return obj;
}
@end

使用如下:

NSArray * array = [NSMutableArray arrayWithArray:@[model,model1]];
NSArray *newArray = [[NSArray alloc]initWithArray:array copyItems:YES];

方法三:使用归档解档 NSCoding协议,重写- (void)encodeWithCoder:(NSCoder *)encoder- (id)initWithCoder:(NSCoder *)decoder

归档解档需要自定义类继承NSCoding协议

#import <objc/runtime.h>

#pragma mark - 归档、解档
- (void)encodeWithCoder:(NSCoder *)encoder {
    //[coder encodeObject:self.title forKey:@"title"];
    //[coder encodeObject:self.code forKey:@"code"];
    Class class = [self class];
    while (class != [NSObject class]) {
        unsigned int count;
        Ivar *ivar = class_copyIvarList(class, &count);
        for (int i = 0; i < count; i++) {
            Ivar iv = ivar[i];
            const char *name = ivar_getName(iv);
            NSString *strName = [NSString stringWithUTF8String:name];
            //利用KVC取值
            id value = [self valueForKey:strName];
            [encoder encodeObject:value forKey:strName];
        }
        free(ivar);
        
        class = class_getSuperclass(class);//记住还要遍历父类的属性
    }
}

- (id)initWithCoder:(NSCoder *)decoder {
    self = [super init];
    if (self) {
        //self.title = [coder decodeObjectForKey:@"title"];
        //self.code = [coder decodeObjectForKey:@"code"];
        Class class = [self class];
        while (class != [NSObject class]) {
            unsigned int count = 0;
            //获取类中所有成员变量名
            Ivar *ivar = class_copyIvarList(class, &count);
            for (int i = 0; i < count; i++) {
                Ivar iva = ivar[i];
                const char *name = ivar_getName(iva);
                NSString *strName = [NSString stringWithUTF8String:name];
                //进行解档取值
                id value = [decoder decodeObjectForKey:strName];
                //利用KVC对属性赋值
                [self setValue:value forKey:strName];
            }
            free(ivar);
            
            class = class_getSuperclass(class);//记住还要遍历父类的属性
        }
    }
    return self;
}

使用方式:

NSArray * newArray = [NSKeyedUnarchiver unarchiveObjectWithData: [NSKeyedArchiver archivedDataWithRootObject:@[model,model1]]];

方法四:NSUserDefaults存储读取自定义对象

存储

//1.自定义类继承NSCoding协议,并实现归档解档,前面介绍过了,不多说

//2.将自定义对象转NSData
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:person];

//3.存到NSUserDefaults
[[NSUserDefaults standardUserDefaults] setObject:data forKey:@"person"];
[[NSUserDefaults standardUserDefaults] synchronize];//这句要写,很多人都忘了

//4.存数组,数组中包含自定义对象
[dataArray addObject:data];//这个data是第2步归档得到的data

//5.NSUserDefaults只能存不可变数组,不要忘了转换成不可变
NSArray * array = [NSArray arrayWithArray:dataArray];
[[NSUserDefaults standardUserDefaults] setObject:array forKey:@"persons"];

读取

NSData *data = [[NSUserDefaults standardUserDefaults] objectForKey:@"person"];
Person *person = [NSKeyedUnarchiver unarchiveObjectWithData:data];

方法五:利用YYModel、MTLModel等三方模型转换

以MTLModel为例:

NSArray *tempArray = [MTLJSONAdapter JSONArrayFromModels:@[model,model1] error:nil];
NSArray *newArray = [MTLJSONAdapter modelsOfClass:[Person class] fromJSONArray:tempArray error:nil];