zoukankan      html  css  js  c++  java
  • IOS中的深拷贝和浅拷贝

    标签:

    什么是深拷贝?什么是浅拷贝?

    为什么经常看到字符串属性要这样定义,那个copy是神马意思?

    @property(nonatomic,copy)NSString* name;
    

     为什么下面的写法是错误的?

    @property(nonatomic,copy)NSMutableString* name;
    

     copyWithZone方法又到底是干嘛用的?

    接下来,我们将一起,一步一步的去揭晓问题的答案。

    Copy到底是个啥?

    其实我们真的没必要把copy想的太高深。它之所以叫copy,其终极目的已不言而喻,不管我们承认与否,它就是和Ctrl+c和Ctrl+v的作用一样。拷贝一个副本出来,使得两个对象直接互不影响,完全独立。

    深拷贝浅拷贝

    copy分为两种,一种是可变copy:MutableCopy。使用它copy出来的对象是可以对其内容进行改变的。另外一种是不可变copy:copy。使用它复制出来的对象,其内容是不可以改变的。

    MutableCopy:可变拷贝,其拷贝过程就是在内存中重新开辟一块区域,将对象复制一份放到这个区域。新对象是可以改变的,而且新对象的改变对源对象是没有影响的。

    copy:不可变copy,同样也是要开辟一段内存空间给新对象,但新对象是不可以改变的。(特殊情况:不可变-》不可变的拷贝,是不创建新内存空间的)

    (注:这里的可变copy和不可变copy是相对Foundation框架的类而言的,如果我们自己定义的类实现copy,一般都是可变的)。

    以上说的这些和深拷贝浅拷贝有什么关系呢?我举一个例子大家立刻就明白了。

    假设我们要对一个不可变的对象进行不可变copy(原来的对象不可变,新对象也不可变)。那么大家觉的,我们还有必要给新对象新建一块内存么?反正大家都不可以对这个对象进行改变,那就统一使用着一个不就可以了么?那么,ios系统怎么处理这个问题呢?引用计数器加1啊。是的,这就是浅拷贝。而需要给新对象开闭内存空间的,就是深拷贝。

    好,接下来我们通过一段代码说明上述问题:

    - (void)viewDidLoad {
        [super viewDidLoad];
        
        //创建一个可变的字符串
        NSMutableString* name = [NSMutableString stringWithFormat:@"好梦园的兔子"];
        NSLog(@"源字符串:%p-%@",name,name);
        
        //可变-》可变
        NSMutableString* name1 = [name mutableCopy];
        [name1 appendString:@"1"];
        NSLog(@"可变字符串:%p-%@",name1,name1);
        
        //可变-》不可变
        NSString* name2 = [name copy];
        NSLog(@"不可变字符串:%p-%@",name2,name2);
        
        
        NSLog(@"___________不可变到不可变______________");
        //不可变-》不可变
        NSString* weibo = @"好梦园的兔子";
        NSLog(@"源字符串:%p-%@",weibo,weibo);
        NSString* weibo1 = [weibo copy];
        NSLog(@"新字符串:%p-%@",weibo1,weibo1);
    }
    

     大家观察运行结果中的对象地址:

    2015-08-15 15:25:12.045 copy的那些事[1207:108085] 源字符串:0x7fede25a2f50-好梦园的兔子
    2015-08-15 15:25:12.046 copy的那些事[1207:108085] 可变字符串:0x7fede25a1690-好梦园的兔子1
    2015-08-15 15:25:12.046 copy的那些事[1207:108085] 不可变字符串:0x7fede25a1b10-好梦园的兔子
    2015-08-15 15:25:12.046 copy的那些事[1207:108085] ___________不可变到不可变______________
    2015-08-15 15:25:12.047 copy的那些事[1207:108085] 源字符串:0x10abe2188-好梦园的兔子
    2015-08-15 15:25:12.047 copy的那些事[1207:108085] 新字符串:0x10abe2188-好梦园的兔子

    Copy属性

    @property(nonatomic,copy)NSString* name;

    我们大家在初学oc的时候,想必都听老师们说过这样一句话,创建属性的时候,遇到字符串括号里就写copy。但这究竟是为什么呢,我问老师,老师说他的老师就是这么说的。呵呵,开个玩笑。

    我们先说明一点,这样做导致的结果就是,你给属性赋值的时候,会进行一次拷贝操作。至于目的嘛,接下来你会找到答案的。废话不多说,上代码:

    /*
     新建一个Student类,里面有两个属性,nameCopy和nameStrong。
    我们接下来就要看看这里的copy到底有什么用
     */
    @interface Student : NSObject
    @property(nonatomic,copy)NSString* nameCopy;
    @property(nonatomic,strong)NSString* nameStrong;
    @end
    - (void)viewDidLoad {
        [super viewDidLoad];
        //新建一个可变的字符串
        NSMutableString* name = [NSMutableString stringWithFormat:@"好梦园的兔子"];
        NSLog(@"源字符串:%p-%@",name,name);
        
        //创建Student对象
        Student * student1 = [[Student alloc]init];
        student1.nameCopy = name;//记住,此时会进行一次copy操作,因为(nonatomic,copy)
        student1.nameStrong = name;
        NSLog(@"nameCopy:%p-%@",student1.nameCopy,student1.nameCopy);
        NSLog(@"nameStrong:%p-%@",student1.nameStrong,student1.nameStrong);
        /*
         通过上述代码,我们发现,(nonatomic,copy)修饰的属性,赋值时进行了copy操作,开辟了一块内存存放该对象属性
         (nonatomic,strong)赋值时,对象属性和源字符串共用了一块内存,只是对源字符串引用计数器+1。
         */
        
        NSLog(@"—————————————源字符串改变—————————————————————");
        //对源字符串进行改变  看看nameCopy和nameStrong哪个会受到影响
        [name setString:@"字符串改变了"];
        NSLog(@"nameCopy:%p-%@",student1.nameCopy,student1.nameCopy);
        NSLog(@"nameStrong:%p-%@",student1.nameStrong,student1.nameStrong);
    }

    运行结果:

    2015-08-15 16:09:07.698 copy的那些事[1504:130239] 源字符串:0x7f830a42fb00-好梦园的兔子
    2015-08-15 16:09:07.699 copy的那些事[1504:130239] nameCopy:0x7f830a42c340-好梦园的兔子
    2015-08-15 16:09:07.699 copy的那些事[1504:130239] nameStrong:0x7f830a42fb00-好梦园的兔子
    2015-08-15 16:09:07.699 copy的那些事[1504:130239] ———————————————————源字符串改变———————————————————
    2015-08-15 16:09:07.699 copy的那些事[1504:130239] nameCopy:0x7f830a42c340-好梦园的兔子
    2015-08-15 16:09:07.700 copy的那些事[1504:130239] nameStrong:0x7f830a42fb00-字符串改变了

    问题:

    下面的写法有问题么?为什么?(下篇博客会为大家做解答)

    @property(nonatomic,copy)NSMutableString* name;

    自定义类Copy

    如果我们希望自己定义的类,也能使用copy方法,嗖嗖嗖就能复制一堆对象,那该如何操作呢。

    假设我们直接使用copy

    Student* student2 =[ student1 copy];

    我们会发现,这时候程序会报错,说我们没有实现copyWithZone方法。这说明,我们是可以为自己的类定义copy方法的,只是要进行一些规范性的操作。

    自定义类实现copy的步骤:

    (1)遵守NSCoping协议
    (2)实现copyWithZone方法。//参数zone基本不用,它的意思是指定该方法从始至终都在某一个区域分配内存
    (所有copy最终都会调用这个方法)
    {
      //方法内部进行以下操作
      //(1)实例化对象
      A* a = [A alloc ]init]; //一般正规写法是[[self.class alloc] init]  因为这样子类也可以复用该方法
      //(2)给属性赋值
      a.xx = xx;
      //(3)返回新对象
      return a;
    }
    上代码:
    #import <Foundation/Foundation.h>
    
    /*
     创建一个Teacher类,并让该类实现copy方法
     */
    
    //1.遵守NSCopying协议
    @interface Teacher : NSObject<NSCopying>
    @property(nonatomic,copy)NSString* name;
    //2.实现copyWithZone方法;
    -(id)copyWithZone:(NSZone *)zone;
    @end
    #import "Teacher.h"
    
    @implementation Teacher
    //实现copyWithZone方法
    -(id)copyWithZone:(NSZone *)zone
    {
        Teacher* teacher = [[self.class alloc]init];
        teacher.name = self.name;
        return teacher;
    }
    @end
    @implementation ViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        Teacher* teacher = [[Teacher alloc]init];
        teacher.name = @"好梦园的兔子";
        
        Teacher* teacher1 = [teacher copy];
        
        NSLog(@"teacher:%p-%@",teacher,teacher.name);
        NSLog(@"teacher1:%p-%@",teacher1,teacher1.name);
    }

    运行结果:

    2015-08-15 16:41:07.821 copy的那些事[1779:148657] teacher:0x7fd750d13ce0-好梦园的兔子
    2015-08-15 16:41:07.822 copy的那些事[1779:148657] teacher1:0x7fd750d13cf0-好梦园的兔子

    上述内容,是将我们自己定义的类实现了copy方法。那么这里有个问题,我可不可以让我自己定义的类实现mutableCopy呢?有没有NSMutableCopying协议呢?

    还有,上述代码中,teacher.name 和teacher1.name 的地址其实是一样的。为什么?

    最后一问答案比较简单,我就替大家回答了(copyWithZone方法中:tescher.name = self.name)

    其他两问,按照实现copy的方法进行尝试,同样会很快得到答案的。

    (完)

  • 相关阅读:
    Spring boot启动后没有生成日志文件问题排错
    keepalived 容器在宿主机重启后无法启动问题:报错:daemon is already running
    【转】iptables命令、规则、参数详解
    【转】VMwareCLI命令参考
    【转】通过ionice和nice降低shell脚本运行的优先级
    【转】dd命令详解及利用dd测试磁盘性能
    【转】Keepalived无法绑定VIP故障排查经历
    【转】浏览器Request Header和Response Header的内容
    【转】Spring Boot 日志配置(超详细)
    Spring数据访问和事务
  • 原文地址:https://www.cnblogs.com/liuzhi20101016/p/5095042.html
Copyright © 2011-2022 走看看