美文网首页iOS DevelopmentiOS Developer
OC中关于copy和mutableCopy

OC中关于copy和mutableCopy

作者: 寻形觅影 | 来源:发表于2018-02-28 16:50 被阅读13次

     这里主要有两个概念深拷贝和浅拷贝,主要涉及两个方法 - (id)copy;- (id)mutableCopy; 和一个属性修饰关键字@copy。

    深拷贝 :即内容拷贝,指拷贝对象的具体内容,而内存地址是重新分配的,两个对象虽然存的值是相同的,但是内存地址不一样,两个对象也互不影响,互不干涉(对于集合类这里有点特殊,下面会详细讲述)。

    浅拷贝:即指针拷贝,是对内存地址的复制,让目标对象指针和源对象指向同一片内存空间。

    一、对象可以实现拷贝的前提

     在OC中,若要使对象可以拥有可拷贝操作,则该对象所属的类必须遵守NSCopyingNSMutableCopy协议,并重写copyWithZone:mutableCopyWithZone:方法。
     对于自定义类一般只实现NSCopying协议,同时如果遵循了NSCopying协议同时实现了copyWithZone:方法,则实例对象调用copy方法([animal copy])时会生成一个新的对象,值得注意的是这是一个深拷贝

    • 若自定义类直接继承于NSObject类,则不需要[super copyWithZone:zone] ;
    • 若自定义类的父类或者超类继承与NSObject,且其父类或超类均未遵循NSCopying协议,则不需要[super copyWithZone:zone] ;
    • 若自定义类的父类或者超类继承与NSObject,且其父类或超类遵循NSCopying协议,则需要[super copyWithZone:zone] ;
    @interface Animal : NSObject
    @property (nonatomic,copy) NSString * name;
    @property (nonatomic,copy) NSString * age;
    @end
    
    @interface Animal()<NSCopying>
    
    @end
    // --------------------------------------------------------
    @implementation Animal
    
    - (id)copyWithZone:(NSZone *)zone
    {
        Animal * animal = [[Animal allocWithZone:zone] init];
        animal.name = self.name;
        animal.age = self.age;
        return animal;
    }
    // -----------------------------------------------------
    //在其他地方调用
    @implementation ViewController
    - (void)viewDidLoad {
        [super viewDidLoad];
    
        Animal * animal = [[Animal alloc] init];
        animal.name = @"男儿何惜死";
        animal.age = @"27";
        Animal * copyAnimal = [animal copy];
        NSLog(@"animal.name/age = %@/%@, copyAnimal.name/age = %@/%@", animal.name, animal.age, copyAnimal.name, copyAnimal.age);
        NSLog(@"animal.address = %p, copyAnimal.address = %p", animal, copyAnimal);  
    }
    @end
    
    二、Copy和MutableCopy的区别

      总的来说:如果不可变对象进行复制,copy是指针复制(浅拷贝)和mutableCopy就是对象复制(深拷贝)。如果是对可变对象复制,都是深拷贝。Copy 返回一个不可变对象的副本,MutalbeCopy返回一个可变对象的副本。至于深拷贝到什么地步,下面针对不同情况一一列举:

    1、NSString、NSMutableString:
    • 普通字符串NSString/NSMutableString使用copy与mutableCopy的区别:
    NSString * string = @"男儿何惜死";
    NSString * copyString = [string copy];
    NSMutableString * mutableCopyStr = [string mutableCopy];
    NSLog(@"string = %p, copyStr = %p mutableCopyStr = %p", string, copyString, mutableCopyStr);
    
    NSMutableString * mutableString = [NSMutableString stringWithString:@"破胆与君尝"];
    NSString * copyMutableStr = [mutableString copy];
    NSMutableString * mutableCopyMutStr = [mutableString mutableCopy];
    NSLog(@"mutableString = %p, copyMutableStr = %p mutableCopyMutStr = %p", mutableString, copyMutableStr, mutableCopyMutStr);
    
    // ------------------------------------------------
    // 打印结果:验证了总结的结论
    string = 0x10e1cf098, copyStr = 0x10e1cf098 mutableCopyStr = 0x600000442520
    mutableString = 0x600000442550, copyMutableStr = 0x600000442580 mutableCopyMutStr = 0x6000004425b0
    
    • 对于NSString类来说当作为属性时,关键字@copy与@strong的区别:
      • 当赋予属性值为不可变字符串时,使用@copy和@strong关键字是没有区别的;
      • 当赋予属性值为可变字符串时,需要特别谨慎使用@copy和@strong;因为使用@strong修饰后属性指针与源指针指向同一块内存区域,当该内存中数据发生变化属性值也会变化,这可能不是我们想要看到的。
    @property (nonatomic,copy) NSString * name;
    @property (nonatomic,strong) NSString * strongName;
    // ----------------------------------------------------
    NSMutableString * mutableString = [NSMutableString stringWithString:@"男儿何惜死"];
    self.name = mutableString;
    self.strongName = mutableString;
    NSLog(@"mutableString = %p, self.name = %p self.strongName = %p", mutableString, self.name, self.strongName);
    NSLog(@"mutableString = %@, self.name = %@ self.strongName = %@", mutableString, self.name, self.strongName);
    [mutableString appendString:@",破胆与君尝"];
    NSLog(@"mutableString = %@, self.name = %@ self.strongName = %@", mutableString, self.name, self.strongName);
    
    // ----------------------------------------------------
    mutableString = 0x600000241a40, self.name = 0x600000241a70 self.strongName = 0x600000241a40
    mutableString = 男儿何惜死, self.name = 男儿何惜死 self.strongName = 男儿何惜死
    mutableString = 男儿何惜死,破胆与君尝, self.name = 男儿何惜死 self.strongName = 男儿何惜死,破胆与君尝
    
    2、NSArray/NSMutableArray、NSDictionary/NSMutableDictionary、NSSet/NSMutableSet等集合类
    (1、)对于常见使用@strong修饰的不可变集合:
    @property (nonatomic,strong) NSArray * testArray;
    // -------------------------------------------------------
    // 示例一:赋予全部是不可变元素的不可变集合
    self.testArray = @[@"不负光阴", @"不负卿"];
    NSArray * test_1 = [self.testArray copy];
    NSMutableArray * test_2 = [self.testArray mutableCopy];
    NSLog(@"self.testArray = %p, test_1 = %p, test_2 = %p", self.testArray, test_1, test_2);
    NSLog(@"\n self.testArray.firstObject = %p, self.testArray.lastObject = %p;
    \n test_1.firstObject = %p, test_1.lastObject = %p;\n test_2.firstObject = %p, test_2.lastObject = %p",
     self.testArray[0], self.testArray[1], test_1[0], test_1[1], test_2[0], test_2[1]);
    // ------------------------------------------------------
    self.testArray = 0x604000039f60, test_1 = 0x604000039f60, test_2 = 0x60400025d130
    self.testArray.firstObject = 0x10f61d0a8, self.testArray.lastObject = 0x10f61d0c8;
    test_1.firstObject = 0x10f61d0a8, test_1.lastObject = 0x10f61d0c8;
    test_2.firstObject = 0x10f61d0a8, test_2.lastObject = 0x10f61d0c8
    // ---------------------------------------------------------
    // 由上可知,虽然mutableCopy是深拷贝,但是不彻底,test_2中的元素与源数组元素依然指向同一块内存区域。
    // 这在数组元素存在可变元素时会发生奇妙的事情。
    
    // 示例二:赋予含有可变元素的不可变集合
    self.testArray = @[[NSMutableString stringWithString:@"不负光阴"], @"不负卿"];
    NSArray * test_1 = [self.testArray copy];
    NSMutableArray * test_2 = [self.testArray mutableCopy];
    NSLog(@"self.testArray = %@, test_1 = %@, test_2 = %@", self.testArray, test_1, test_2);
    NSLog(@"self.testArray = %p, test_1 = %p, test_2 = %p", self.testArray, test_1, test_2);
    NSLog(@"\n self.testArray.firstObject = %p, self.testArray.lastObject = %p;
    \n test_1.firstObject = %p, test_1.lastObject = %p;\n test_2.firstObject = %p, test_2.lastObject = %p",
     self.testArray[0], self.testArray[1], test_1[0], test_1[1], test_2[0], test_2[1]);
    NSMutableString * firstStr = [self.testArray firstObject];
    [firstStr appendString:@",不负自己"];
    NSLog(@"self.testArray = %@, test_1 = %@, test_2 = %@", self.testArray, test_1, test_2);
    // --------------------------------------------------------
    self.testArray =    (
        "不负光阴",
        "不负卿",
    ),
     test_1 =   (
        "不负光阴",
        "不负卿",
    ), 
    test_2 =    (
        "不负光阴",
        "不负卿",
    )
    
    self.testArray = 0x60400022bf80, test_1 = 0x60400022bf80, test_2 = 0x604000445100
    self.testArray.firstObject = 0x604000442430, self.testArray.lastObject = 0x1030c8df8;
     test_1.firstObject = 0x604000442430, test_1.lastObject = 0x1030c8df8;
     test_2.firstObject = 0x604000442430, test_2.lastObject = 0x1030c8df8
    
    self.testArray =    (
        "不负光阴,不负自己",
        "不负卿",
    ), 
    test_1 =    (
        "不负光阴,不负自己",
        "不负卿",
    ), 
    test_2 =    (
        "不负光阴,不负自己",
        "不负卿",
    )
    
    // --------------------------------------------------------
    // 可以看到所有数组的第一个元素全部发生了改变。同样数组内即使存在可变数元素,外部使用mutableCopy,内部元素依然指向同一块内存区域!
    // 试想一下如果 将[firstStr appendString:@",不负自己"];
    // 改为 firstStr = [NSMutableString stringWithString:@"不负光阴,不负自己"];
    //打印结果如何?为什么?
    
    // 示例三:赋予可变集合(这里与NSString相似)
    NSMutableArray * baseArray = [NSMutableArray arrayWithArray:@[@"不负光阴", @"不负卿"]];
    self.testArray = baseArray;
    NSArray * test_1 = [self.testArray copy];
    NSMutableArray * test_2 = [self.testArray mutableCopy];
    NSLog(@"baseArray = %p, self.testArray = %p, test_1 = %p, test_2 = %p", baseArray, self.testArray, test_1, test_2);
    NSLog(@"baseArray = %@, self.testArray = %@", baseArray, self.testArray);
    [baseArray addObject:@"不负自己"];
    NSLog(@"baseArray = %@, self.testArray = %@", baseArray, self.testArray);
    // ----------------------------------------------------------
    
    baseArray = 0x6000004442f0, self.testArray = 0x6000004442f0, test_1 = 0x6000000342e0, test_2 = 0x600000444320
    baseArray =     (
        "不负光阴",
        "不负卿",
    ), self.testArray =     (
        "不负光阴",
        "不负卿",
    )
    baseArray =     (
        "不负光阴",
        "不负卿",
        "不负自己",
    ), self.testArray =     (
        "不负光阴",
        "不负卿",
        "不负自己",
    )
    // ----------------------------------------------------------
    // 赋值操作是一个浅拷贝的过程;
    // 可以看到当源数据发生变化时self.testArray也与之变化,这可能也不是我们想看到到~
    
    
    (2、)使用@copy关键字修饰的不可变集合:

     当赋予其不可变集合对象时与@strong没有区别,对其进行copy或者mutableCopy操作与(1、)中示例一、二情况一致,即copy操作生成的对象与原对象指向同一块内存区域,mutableCopy则不同;
     当对其赋予可变集合时,由于是@copy修饰,此时虽然是赋值操作同时也是一个深拷贝的过程,会在一个新的内存地址来存储@copy修饰的集合,集合内元素的地址与源集合中元素地址依然一样,但是此时再修改源集合,目标集合不会再改变!

    @property (nonatomic,copy) NSArray * testCopyArray;
    // -------------------------------------------------------------------
    NSMutableArray * baseArray = [NSMutableArray arrayWithArray:@[@"不负光阴", @"不负卿"]];
    self.testCopyArray = baseArray;
    NSArray * test_1 = [self.testCopyArray copy];
    NSMutableArray * test_2 = [self.testCopyArray mutableCopy];
    NSLog(@"baseArray = %p, self.testCopyArray = %p, test_1 = %p, test_2 = %p", baseArray, self.testCopyArray, test_1, test_2);
    NSLog(@"baseArray = %@, self.testCopyArray = %@", baseArray, self.testCopyArray);
    [baseArray addObject:@"不负自己"];
    NSLog(@"baseArray = %@, self.testCopyArray = %@", baseArray, self.testCopyArray);
    // -------------------------------------------------------------------
     baseArray = 0x60400025de80, self.testCopyArray = 0x60400022bb60, test_1 = 0x60400022bb60, test_2 = 0x60400025deb0
     baseArray =    (
        "不负光阴",
        "不负卿",
    ), self.testCopyArray =     (
        "不负光阴",
        "不负卿",
    )
    baseArray =     (
        "不负光阴",
        "不负卿",
        "不负自己",
    ), self.testCopyArray =     (
        "不负光阴",
        "不负卿",
    )
    // -------------------------------------------------------------------
    // 是不是觉得使用@copy修饰的不可变集合更靠谱一些😂
    // 同时需要注意的是:对于不可变数组,不管用什么修饰,不管给予其赋值的是可变还是不可变的数组,copy操作生成的对象与原对象均指向同一块内存区域!!!
    
    (3、)使用@strong修饰的可变集合:
    // 与(1、)中的第三种情况(示例三)相似
    @property (nonatomic,strong) NSMutableArray * testArray;
     // ---------------------------------------------------------------
    NSMutableArray * baseArray = [NSMutableArray arrayWithArray:@[@"不负光阴", @"不负卿"]];
    self.testArray = baseArray;
    NSArray * test_1 = [self.testArray copy];
    NSMutableArray * test_2 = [self.testArray mutableCopy];
    NSLog(@"baseArray = %p, self.testArray = %p, test_1 = %p, test_2 = %p", baseArray, self.testArray, test_1, test_2);
    NSLog(@"baseArray = %@, self.testArray = %@", baseArray, self.testArray);
    [baseArray addObject:@"不负自己"];
    NSLog(@"baseArray = %@, self.testArray = %@", baseArray, self.testArray);
    // -------------------------------------------------------------------
    baseArray = 0x6000004496c0, self.testArray = 0x6000004496c0, test_1 = 0x6000000307c0, test_2 = 0x6000004496f0
    baseArray =     (
        "不负光阴",
        "不负卿",
    ), self.testArray =     (
        "不负光阴",
        "不负卿",
    )
    baseArray =     (
        "不负光阴",
        "不负卿",
        "不负自己",
    ), self.testArray =     (
        "不负光阴",
        "不负卿",
        "不负自己",
    )
    
    (4、)使用@copy修饰的可变集合:
    // 为其赋值等操作均没啥问题,问题出在将其作为一个值,赋予其他可变集合时!
    @property (nonatomic,copy) NSMutableArray * testCopyArray;
    // -------------------------------------------------------------------
    self.testCopyArray = [NSMutableArray arrayWithArray:@[@"不负光阴", @"不负卿"]];
    NSMutableArray * baseArray = [NSMutableArray array];
    baseArray = self.testCopyArray;
    NSLog(@"baseArray = %p, self.testCopyArray = %p", baseArray, self.testCopyArray);
    [self.testCopyArray addObject:@"不负自己"];
    [baseArray addObject:@"不负自己"];
    // -------------------------------------------------------------------
    //此时会崩溃
    baseArray = 0x60400022acc0, self.testCopyArray = 0x60400022acc0
    -[__NSArrayI addObject:]: unrecognized selector sent to instance 0x60400022acc0
    
    // 可以看到baseArray已经是一个不可变的集合,所以对其进行增删等操作会引发异常。
    // 原因就是因为修饰词是@copy,我们上面已经强调了copy操作返回不可变对象,
    // mutableCopy返回可变对象,这与是否是深拷贝与浅拷贝无关,要切记。
    

     以上均是用数组举例,其实字典等也相似的,可以自行尝试一下。这里给出一个简单的🌰

    @property (nonatomic,strong) NSDictionary * testDic;
    // ---------------------------------------------------------------------
    NSMutableDictionary * dic = [NSMutableDictionary dictionaryWithDictionary:@{@"001" : @"男儿何惜死", @"002" : @"破胆与君尝"}];
    self.testDic = dic;
    NSDictionary * test_1 = [self.testDic copy];
    NSMutableDictionary * test_2 = [self.testDic mutableCopy];
    NSLog(@"dic = %p, self.testDic = %p, test_1 = %p, test_2 = %p", dic, self.testDic, test_1, test_2);
    NSLog(@"dic = %@, self.testDic = %@", dic, self.testDic);
    [dic setObject:@"两情若是久长时" forKey:@"003"];
    NSLog(@"dic = %@, self.testDic = %@", dic, self.testDic);
    // ---------------------------------------------------------------------
    dic = 0x604000030320, self.testDic = 0x604000030320, test_1 = 0x604000030340, test_2 = 0x604000030360
    
    dic =   {
        001 = "男儿何惜死",
        002 = "破胆与君尝",
    }, self.testDic =   {
        001 = "男儿何惜死",
        002 = "破胆与君尝",
    }
    
    dic =   {
        001 = "男儿何惜死",
        002 = "破胆与君尝",
        003 = "两情若是久长时",
    }, self.testDic =   {
        001 = "男儿何惜死",
        002 = "破胆与君尝",
        003 = "两情若是久长时",
    }
    
    三、集合的真正的深拷贝

     上面提到集合的深拷贝并不彻底,集合内的元素的地址依然是相同的,那么如何实现集合的真正的拷贝呢?参考文档

    // 可以将该集合归档然后再解档,前提是内容均符合NSCoding协议,这样可以得到一个真正意义上的深拷贝。
    @property (nonatomic,strong) NSArray * testArray;
    // ---------------------------------------------------------------------
    self.testArray = @[@"不负光阴", @"不负卿"];
    NSArray * deepArray = [NSKeyedUnarchiver unarchiveObjectWithData:[NSKeyedArchiver archivedDataWithRootObject:self.testArray]];
    NSLog(@"self.testArray = %p, deepArray = %p", self.testArray, deepArray);
    NSLog(@"self.testArray = %@, deepArray = %@", self.testArray, deepArray);
    NSLog(@"self.testArray[0] = %p, self.testArray[1] = %p, deepArray[0] = %p, deepArray[1] = %p", self.testArray[0], self.testArray[1], deepArray[0], deepArray[1]);
    // ---------------------------------------------------------------------
    self.testArray = 0x604000229b60, deepArray = 0x604000229be0
    self.testArray =    (
        "不负光阴",
        "不负卿",
    ), deepArray =  (
        "不负光阴",
        "不负卿",
    )
    self.testArray[0] = 0x10350d238, self.testArray[1] = 0x10350d258, deepArray[0] = 0x604000229c20, deepArray[1] = 0x604000229b80
    

    相关文章

      网友评论

        本文标题:OC中关于copy和mutableCopy

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