文章目录
什么是深拷贝,什么是浅拷贝?
深拷贝就是内容拷贝,浅拷贝就是指针拷贝。
深拷贝就是拷贝出和原来仅仅是值一样,但是内存地址完全不一样的新的对象,创建后和原对象没有任何关系。浅拷贝就是拷贝指向原来对象的指针,使原对象的引用计数+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);
讲义中有这样几句话:
- copy方法用于复制对象的副本,mutableCopy方法用于复制对象的可变副本
- 例如程序调用NSMutableString的copy方法,将会返回不可修改的字符串对象,程序调用NSString的mutableCopy方法,将会返回一个NSMutableString对象
- 无论如何,copy和mutableCopy返回的总是原对象的副本,当程序对复制的副本进行修改时,原对象通常不会受到影响。
所以在上面的代码中进行给可变数组进行append的可变数组的添加操作
所以可以得出结论
- 可变对象copy后返回的对象是不可变的,mutableCopy后返回的对象是可变的
- 对于可变对象的复制,都是深拷贝 (地址都发生改变了)
二、对于容器类对象的深拷贝、浅拷贝
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]);
结果:
可以得出下列结论:
- copyArray01和array01指向的是同一个对象,包括里面的元素也是指向相同的地址。
- mutableCopyArray01和array01指向的是不同的对象,但是里面的元素指向相同的对象,mutableCopyArray01可以修改自己的对象。
- 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的结果:
由上列代码可得:
- 因为这里的otherName是可变的,person.name的属性是copy,所以创建了新的字符串,属于深拷贝
- 如果person.name设置为strong,那么就是浅拷贝。因为strong会持有原来的对象,使原来的对象的引用计数+1,其实就是指针拷贝。所以这里用strong还是copy,取决于实际需求。
3.
当otherName为NSString型时,无论是属性strong还是copy ,赋值方法是拷贝还是直接赋值,都是浅拷贝。
综上所述,NSString使用copy还是strong也有了定论
-
当原字符串是NSString时,字符串是不可变的,不管是Strong还是Copy属性的对象,都是指向原对象,Copy操作只是做了次浅拷贝。
-
当原字符串是NSMutableString时,Strong属性只是增加了原字符串的引用计数,而Copy属性则是对原字符串做了次深拷贝,产生一个新的对象,且Copy属性对象指向这个新的对象,且这个Copy属性对象的类型始终是NSString,而不是NSMutableString,因此其是不可变的。
-
这里还有一个性能问题,即在原字符串是NSMutableString,Strong是单纯的增加对象的引用计数,而Copy操作是执行了一次深拷贝,所以性能上会有所差异(虽然不大)。如果原字符串是NSString时,则没有这个问题。
所以,在声明NSString属性时,到底是选择strong还是copy,可以根据实际情况来定。不过,一般我们将对象声明为NSString时,都不希望它改变,所以大多数情况下,我们建议用copy,以免因可变字符串的修改导致的一些非预期问题。