[iOS开发]深拷贝与浅拷贝

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

深拷贝就是内容拷贝,浅拷贝就是指针拷贝。
深拷贝就是拷贝出和原来仅仅是值一样,但是内存地址完全不一样的新的对象,创建后和原对象没有任何关系。浅拷贝就是拷贝指向原来对象的指针,使原对象的引用计数+1,可以理解为创建了一个指向原对象的新指针而已,并没有创建一个全新的对象。

按类型进行说明判断:

一、非容器类对象的深拷贝、浅拷贝

1. 不可变型字符串是直接赋值的,右侧如果是copy,那么就是浅拷贝;右侧如果是mutableCopy,那么就是深拷贝。
 
    NSString *string1 = @"helloworld";
    NSString *string2 = [string1 copy]; 
    NSString *string3 = [string1 mutableCopy]; 
    NSMutableString *string4 = [string1 copy]; 
    NSMutableString *string5 = [string1 mutableCopy];
	NSLog(@"string1 = %p;string2 = %p;string3 = %p;string4 = %p;string5 = %p",string1,string2,string3,string4,string5);

我们可以看到2 4字符串的地址相同
在这里插入图片描述

使用stringWithFormat创建字符串,而不是直接赋值结论一致

2. 如果是一个MutableString可变类型的字符串,那么无论是copy,mutableCopy,都会创建一个新对象。
 	   NSMutableString *string1 = [NSMutableString stringWithString:@"helloworld"];
       NSString *string2 = [string1 copy]; 
       NSString *string3 = [string1 mutableCopy]; 
       NSMutableString *string4 = [string1 copy]; 
       NSMutableString *string5 = [string1 mutableCopy];
    
       NSLog(@"string1 = %p;string2 = %p;string3 = %p;string4 = %p;string5 = %p",string1,string2,string3,string4,string5);

在这里插入图片描述

讲义中有这样几句话:

  1. copy方法用于复制对象的副本,mutableCopy方法用于复制对象的可变副本
  2. 例如程序调用NSMutableString的copy方法,将会返回不可修改的字符串对象,程序调用NSString的mutableCopy方法,将会返回一个NSMutableString对象
  3. 无论如何,copy和mutableCopy返回的总是原对象的副本,当程序对复制的副本进行修改时,原对象通常不会受到影响。

所以在上面的代码中进行给可变数组进行append的可变数组的添加操作
在这里插入图片描述
在这里插入图片描述

所以可以得出结论

  1. 可变对象copy后返回的对象是不可变的,mutableCopy后返回的对象是可变的
  2. 对于可变对象的复制,都是深拷贝 (地址都发生改变了)

二、对于容器类对象的深拷贝、浅拷贝

1. 不可变的容器对象
NSArray *array01 = [NSArray arrayWithObjects:@"a",@"b",@"c", nil];
        NSArray *copyArray01 = [array01 copy];
        NSMutableArray *mutableCopyArray01 = [array01 mutableCopy];
        NSLog(@"array01 = %p,copyArray01 = %p",array01,copyArray01);
        NSLog(@"array01 = %p,mutableCopyArray01 = %p",array01,mutableCopyArray01);
//-----------------------------------------------------
        NSLog(@"array01[0] = %p,array01[1] = %p,array01[2] = %p",array01[0],array01[1],array01[2]);
        NSLog(@"copyArray01[0] = %p,copyArray01[1] = %p,copyArray01[2] = %p",copyArray01[0],copyArray01[1],copyArray01[2]);
        NSLog(@"mutableCopyArray01[0] = %p,mutableCopyArray01[1] = %p,mutableCopyArray01[2] = %p",mutableCopyArray01[0],mutableCopyArray01[1],mutableCopyArray01[2]);

结果:
在这里插入图片描述

可以得出下列结论:

  1. copyArray01和array01指向的是同一个对象,包括里面的元素也是指向相同的地址。
  2. mutableCopyArray01和array01指向的是不同的对象,但是里面的元素指向相同的对象,mutableCopyArray01可以修改自己的对象。
  3. copyArray01是对array01的指针复制(浅复制),而mutableCopyArray01是内容复制。

所以这就是不可变的容器对象 copy是浅拷贝 mutablecopy是深拷贝

2.对于可变的容器对象
NSMutableArray *array01 = [NSMutableArray arrayWithObjects:@"a",@"b",@"c", nil];
        NSArray *copyArray01 = [array01 copy];
        NSMutableArray *mutableCopyArray01 = [array01 mutableCopy];
        NSLog(@"array01 = %p,copyArray01 = %p",array01,copyArray01);
        NSLog(@"array01 = %p,mutableCopyArray01 = %p",array01,mutableCopyArray01);
    
    
        NSLog(@"array01[0] = %p,array01[1] = %p,array01[2] = %p",array01[0],array01[1],array01[2]);
        NSLog(@"copyArray01[0] = %p,copyArray01[1] = %p,copyArray01[2] = %p",copyArray01[0],copyArray01[1],copyArray01[2]);
        NSLog(@"mutableCopyArray01[0] = %p,mutableCopyArray01[1] = %p,mutableCopyArray01[2] = %p",mutableCopyArray01[0],mutableCopyArray01[1],mutableCopyArray01[2]);

在这里插入图片描述

我们可以看出来 对于可变对象的拷贝都是一个新对象,深拷贝。对于数组而言,元素对象始终是浅拷贝。与非容器类相同

集合对象的内容复制仅限于对象本身,对象元素仍然是指针复制。
那么如何进行完全深复制呢?
我们在这里提供两种做法
第一种

NSDictionary shallowCopyDict = [[NSDictionary alloc] initWithDictionary:someDictionary copyItems:YES];

如果你用这种方法深复制,集合里的每个对象都会收到 copyWithZone: 消息。

如果集合里的对象遵循 NSCopying 协议,那么对象就会被深复制到新的集合。

如果对象没有遵循 NSCopying 协议,而尝试用这种方法进行深复制,会在运行时出错。

copyWithZone: 这种拷贝方式只能够提供一层内存拷贝(one-level-deep copy),而非真正的深复制。

第二个方法是将集合进行归档(archive),然后解档(unarchive),
如:

NSArray *trueDeepCopyArray = [NSKeyedUnarchiver unarchiveObjectWithData:[NSKeyedArchiver archivedDataWithRootObject:oldArray]];

三、对于自定义的类的拷贝

1. Person.h
#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

@interface Person : NSObject
@property (nonatomic, strong) NSString *name;
@property (nonatomic, assign) NSInteger age;
@end

NS_ASSUME_NONNULL_END

Person.m
为什么要在Person.m中进行copyWithZone和mutableCopyWithZone的重写呢?
iOS中并不是所有的对象都支持copy、mutableCopy方法,只有遵守NSCopying协议的类可以发送copy消息,遵守NSMutableCopying协议的类才可以发送mutableCopy消息,否则就会崩溃。
在这里插入图片描述
而解决这个崩溃的方案就是重写copyWithZone和mutableCopyWithZone方法


#import "Person.h"
@interface Person()<NSCopying,NSMutableCopying>

@end

@implementation Person

- (id)copyWithZone:(NSZone *)zone {
    Person *person = [[Person allocWithZone:zone]init];
    person.name = self.name;
    person.age = self.age;
    return person;
}


- (nonnull id)mutableCopyWithZone:(nullable NSZone *)zone {
    Person *person = [[Person allocWithZone:zone] init];
        person.name = self.name;
        person.age = self.age;
        return person;

}

@end

进行深、浅拷贝测试

		Person *person = [[Person alloc] init];
        person.name = @"Jack";
        person.age = 23;
     
        Person *copyPerson = [person copy]; // 深拷贝
        Person *mutableCopyPerson = [person mutableCopy]; // 深拷贝
        NSLog(@"person = %p;copyPerson = %p",person,copyPerson);
        NSLog(@"person = %p;mutableCopyPerson = %p",person,mutableCopyPerson);

在这里插入图片描述

我们可以看到无论是copy还是mutableCopy,都是深拷贝,这是因为我们本来就是使用copyWithZone和mutableCopyWithZone进行拷贝的。

2. 分别对.m文件中的name属性使用copy和strong
  NSMutableString *otherName = [[NSMutableString alloc] initWithString:@"Jack"];
       Person *person = [[Person alloc] init];
       person.name = otherName;
       person.age = 23;
    
       [otherName appendString:@" and Mary"];
       NSLog(@"person.name = %@",person.name);

copy的结果:
在这里插入图片描述

strong的结果:
在这里插入图片描述

由上列代码可得:

  1. 因为这里的otherName是可变的,person.name的属性是copy,所以创建了新的字符串,属于深拷贝
  2. 如果person.name设置为strong,那么就是浅拷贝。因为strong会持有原来的对象,使原来的对象的引用计数+1,其实就是指针拷贝。所以这里用strong还是copy,取决于实际需求。

3.

当otherName为NSString型时,无论是属性strong还是copy ,赋值方法是拷贝还是直接赋值,都是浅拷贝。

综上所述,NSString使用copy还是strong也有了定论

  1. 当原字符串是NSString时,字符串是不可变的,不管是Strong还是Copy属性的对象,都是指向原对象,Copy操作只是做了次浅拷贝。

  2. 当原字符串是NSMutableString时,Strong属性只是增加了原字符串的引用计数,而Copy属性则是对原字符串做了次深拷贝,产生一个新的对象,且Copy属性对象指向这个新的对象,且这个Copy属性对象的类型始终是NSString,而不是NSMutableString,因此其是不可变的。

  3. 这里还有一个性能问题,即在原字符串是NSMutableString,Strong是单纯的增加对象的引用计数,而Copy操作是执行了一次深拷贝,所以性能上会有所差异(虽然不大)。如果原字符串是NSString时,则没有这个问题。

所以,在声明NSString属性时,到底是选择strong还是copy,可以根据实际情况来定。不过,一般我们将对象声明为NSString时,都不希望它改变,所以大多数情况下,我们建议用copy,以免因可变字符串的修改导致的一些非预期问题。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值