美文网首页
iOS 浅谈model 深浅copy

iOS 浅谈model 深浅copy

作者: 目染江夏 | 来源:发表于2017-04-30 08:25 被阅读657次

    在我们开发中常常会遇到这样的情况,当前是列表页面 ,点击后进详情页面,会把列表页面的model传到详情页面,如图:

    列表页面 详情页面

    有时候可能会在详情页做修改,

    详情页 列表页面

    这样就会影响列表页面数据 。

    ///思考: 一开始我觉得是 数组的问题,传过去的数组应该是需要copy一下,测试结果发现,数组指针地址不同,但是里面的model指针确实相同的,所以需要把model copy一下,鉴于之前遇到过modelcopy问题,如下是解决问题的方法:


    ```

    //创建数据

    for (NSInteger index = 0; index< 5; index++) {

    MyModel *myModel = [[MyModel alloc]init];

    myModel.name = [NSString stringWithFormat:@"我是:%ld",index];

    [_modelArr addObject:myModel];

    }

    //页面传值

    MineViewController *mine = [MineViewController new];

    mine.muArr = _modelArr;

    ```

    //重点 model 要 写copy协议//model .h 中 签协议@interface MyModel : NSObject//model .m 中重写方法

    - (id)copyWithZone:(NSZone *)zone{

    MyModel * model = [[MyModel allocWithZone:zone] init];

    model.name =self.name ;

    return model;

    }

    - (id)mutableCopyWithZone:(NSZone *)zone{

    MyModel * model = [[MyModel allocWithZone:zone] init];

    model.name =self.name ;

    return model;

    }

    //详情页面 初始化数据

    NSMutableArray *muArr = [NSMutableArray new];

    for (NSInteger index = 0; index < self.muArr.count; index++) {

    MyModel *model = [self.muArr[index] copy];

    [muArr addObject:model];

    }

    self.muArr =  muArr;


    这样就不会影响列表页面的数据了。


    优化:model属性过多的时候 一个一个写比较麻烦,我这里用到了一个比较好的方法 。

    #import@interface PlayModel : NSObject@property(nonatomic,strong)NSNumber *num;

    @property(nonatomic,strong)NSDictionary *dic;

    @property(nonatomic,assign)BOOL isOpen;

    @property(nonatomic,copy)NSString *age;

    @property(nonatomic,copy)NSString *name;

    @property(nonatomic,copy)NSString *age2;

    @property(nonatomic,copy)NSString *name2;

    @property(nonatomic,copy)NSString *age3;

    @property(nonatomic,copy)NSString *name3;

    - (NSArray *) allPropertyNames;

    - (id ) displayCurrentModlePropertyBy:(NSString *)propertyName;

    @end

    //.m  中

    #import<objc/runtime.h>

    - (id)copyWithZone:(NSZone *)zone {

    PlayModel *instance = [[PlayModel allocWithZone:zone] init];

    NSArray *modelNames =    [self allPropertyNames];

    for (NSInteger index = 0; index < modelNames.count; index ++) {

    [instance setValue:[self displayCurrentModlePropertyBy:modelNames[index]] forKey:modelNames[index]];

    }

    return instance;

    }

    - (id)mutableCopyWithZone:(NSZone *)zone {

    PlayModel *instance = [[PlayModel allocWithZone:zone] init];

    NSArray *modelNames =    [self allPropertyNames];

    for (NSInteger index = 0; index < modelNames.count; index ++) {

    [instance setValue:[self displayCurrentModlePropertyBy:modelNames[index]] forKey:modelNames[index]];

    }

    return instance;

    }

    ///通过运行时获取当前对象的所有属性的名称,以数组的形式返回

    - (NSArray *) allPropertyNames{

    ///存储所有的属性名称

    NSMutableArray *allNames = [[NSMutableArray alloc] init];

    ///存储属性的个数

    unsigned int propertyCount = 0;

    ///通过运行时获取当前类的属性

    objc_property_t  * propertys = class_copyPropertyList([self class], &propertyCount);

    //把属性放到数组中

    for (int i = 0; i < propertyCount; i ++) {

    ///取出第一个属性

    objc_property_t property = propertys[i];

    const char * propertyName = property_getName(property);

    [allNames addObject:[NSString stringWithUTF8String:propertyName]];

    }

    ///释放

    free(propertys);

    return allNames;

    }

    #pragma mark -- 通过字符串来创建该字符串的Setter方法,并返回

    - (SEL) creatGetterWithPropertyName: (NSString *) propertyName{

    //1.返回get方法: oc中的get方法就是属性的本身

    return NSSelectorFromString(propertyName);

    }

    //获取

    - (id) displayCurrentModlePropertyBy:(NSString *)propertyName{

    //接收返回的值

    NSObject *__unsafe_unretained returnValue = nil;

    //获取get方法

    SEL getSel = [self creatGetterWithPropertyName:propertyName];

    NSLog(@"propertyName : %@",propertyName);

    if ([self respondsToSelector:getSel]) {

    //获得类和方法的签名

    NSMethodSignature *signature = [self methodSignatureForSelector:getSel];

    //从签名获得调用对象

    NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];

    //设置target

    [invocation setTarget:self];

    //设置selector

    [invocation setSelector:getSel];

    //调用

    [invocation invoke];

    //获得返回值类型

    const char *returnType = signature.methodReturnType;

    //如果没有返回值,也就是消息声明为void,那么returnValue=nil

    if( !strcmp(returnType, @encode(void)) ){

    returnValue =  nil;

    }

    //如果返回值为对象,那么为变量赋值

    else if( !strcmp(returnType, @encode(id)) ){

    [invocation getReturnValue:&returnValue];

    }

    else{

    //如果返回值为普通类型NSInteger  BOOL

    //返回值长度

    NSUInteger length = [signature methodReturnLength];

    //根据长度申请内存

    void *buffer = (void *)malloc(length);

    //为变量赋值

    [invocation getReturnValue:buffer];

    if( !strcmp(returnType, @encode(BOOL)) ) {

    returnValue = [NSNumber numberWithBool:*((BOOL*)buffer)];

    }

    else if( !strcmp(returnType, @encode(NSInteger)) ){

    returnValue = [NSNumber numberWithInteger:*((NSInteger*)buffer)];

    }else{

    returnValue = [NSValue valueWithBytes:buffer objCType:returnType];

    }

    }

          //接收返回值

    //        [invocation getReturnValue:&returnValue];

    NSLog(@"returnValue  : %@",returnValue);

    }

    return  returnValue ;

    }


    结语:iOS中model需要重写copy 方法才能实现深copy,默认的情况下传的是指针地址;

    一般在列表创建的数据源,到详情页面需要深copy一下,防止发在详情页面修改了数据影响上一层的数据。一位老司机说过,每个页面从后台获取的数据能获取的尽量获取,不要从上个页面传递。

    ️五一愉快。


    相关文章

      网友评论

          本文标题:iOS 浅谈model 深浅copy

          本文链接:https://www.haomeiwen.com/subject/nzittxtx.html