假设我们要对一个不可变的对象进行不可变copy(原来的对象不可变,新对象也不可变)。就没必要给新对象新建一块内存,反正大家都不可以对这个对象进行改变,那都使用一个就可以。所以iOS系统规定浅拷贝引用计数器加1就行。而需要给新对象开闭内存空间的,就是深拷贝。
copy得到的类型一定是不可变的;mutableCopy得到的类型一定是可变的
使用mutable,都是深拷贝(不管是拷贝类型还是拷贝方法);但是copy也有深拷贝;
一. 对于不可变对象
//copy, 浅拷贝(指针复制)
//mutableCopy,深拷贝(对象复制 指针+内存),返回对象可变(产生新的可变对象)
- (void)imutableInstanceCopy
{
NSString *string = @"Hello";
//copy,浅拷贝(拷贝出一个新的指针,指向string指向的内存地址)
NSString *stringCopy = [string copy];
//mutableCopy,深拷贝(拷贝出一个新的指针,并且拷贝一份string指向的内存并指向它,不指向string指向的内存地址)
NSMutableString *stringMutableCopy = [string mutableCopy];
[stringMutableCopy appendString:@"!"];
NSLog(@"string: %@ , %p", string,string);
NSLog(@"strongCopy: %@ , %p", stringCopy,stringCopy);
NSLog(@"stringMCopy:%@ , %p", stringMutableCopy,stringMutableCopy);
//结论://string与stringCopy的内存地址相同,俩不同的指针指向同一个地址;
//string与stringMutableCopy的内存地址不同,分配了新的内存
}
对不可变对象:
2018-02-26 18:36:57.984922+0800 深浅拷贝[31542:2723909] string: Hello , 0x105820078
2018-02-26 18:36:57.985022+0800 深浅拷贝[31542:2723909] strongCopy: Hello , 0x105820078
2018-02-26 18:36:57.985113+0800 深浅拷贝[31542:2723909] stringMCopy:Hello! , 0x60400005b300
二、对可变对象复制,都是深拷贝,但是copy关键字返回的对象是不可变的
//copy, 深拷贝(对象复制),返回对象不可变
//mutableCopy,深拷贝(对象复制),返回对象可变
- (void)mutableInstanceCopy
{
NSMutableString *mutableString = [NSMutableString stringWithString: @"Hello"];
//copy 深拷贝,返回对象不可变
id mutableStringCopy = [mutableString copy];
//运行时,此句会报错,错误信息“Attempt to mutate immutable object with appendString:”
// [mutableStringCopy appendString:@"~~~"];
//mutableCopy 深拷贝,返回对象可变
NSMutableString *mutableStringMutableCopy = [mutableString mutableCopy];
[mutableStringMutableCopy appendString:@"!"];
//内存地址都不同
NSLog(@"mutableString: %@ , %p", mutableString,mutableString);
NSLog(@"mutableStringCopy:%@ , %p", mutableStringCopy,mutableStringCopy);
NSLog(@"mutableStringMutableCopy:%@ , %p", mutableStringMutableCopy,mutableStringMutableCopy);
}
对可变对象:
2018-02-26 18:41:23.806579+0800 深浅拷贝[31596:2729403] mutableString: Hello , 0x60c00025e5a0
2018-02-26 18:41:23.806781+0800 深浅拷贝[31596:2729403] mutableStringCopy:Hello , 0xa00006f6c6c65485
2018-02-26 18:41:23.806883+0800 深浅拷贝[31596:2729403] mutableStringMutableCopy:Hello! , 0x60c00025e6c0
三、容器对象(NSArray,NAMutableArray;NSDictionary,NSMutableDictionary;NSSet集合)遵循非容器对象的拷贝原则
//注意容器里套可变对象的情况(容器内的元素都是浅拷贝)
//开发时注意,数组中都是模型时,拷贝后的数组中的模型还是原来数组的模型,一个变都改变
- (void)containerInstanceShallowCopy
{
NSArray *array = [NSArray arrayWithObjects:[NSMutableString stringWithString:@"Welcome"],@"to",@"Xcode",nil];
//未创建了新的容器,容器内的元素是指针赋值(浅拷贝)
NSArray *arrayCopy = [array copy];
//创建了新的容器,容器内的元素是指针赋值(浅拷贝)
NSMutableArray *arrayMutableCopy = [array mutableCopy];
NSLog(@"array: %p", array);
NSLog(@"arrayCopy: %p", arrayCopy);
NSLog(@"arrayMutableCopy: %p", arrayMutableCopy);
//容器内的对象是浅拷贝,即它们在内存中只有一份
NSMutableString *testString = [array objectAtIndex:0];
[testString appendString:@" you"];
//三个数组的内容同时改变
NSLog(@"array[0]: %@", array[0]);
NSLog(@"arrayCopy[0]: %@", arrayCopy[0]);
NSLog(@"arrayMutableCopy[0]: %@", arrayMutableCopy[0]);
}
对容器对象:
2018-02-26 18:44:28.818598+0800 深浅拷贝[31639:2733020] array: 0x604000445010
2018-02-26 18:44:28.818719+0800 深浅拷贝[31639:2733020] arrayCopy: 0x604000445010
2018-02-26 18:44:28.818797+0800 深浅拷贝[31639:2733020] arrayMutableCopy: 0x604000445a00
2018-02-26 18:44:28.818873+0800 深浅拷贝[31639:2733020] array[0]: Welcome you
2018-02-26 18:44:28.818926+0800 深浅拷贝[31639:2733020] arrayCopy[0]: Welcome you
2018-02-26 18:44:28.818998+0800 深浅拷贝[31639:2733020] arrayMutableCopy[0]: Welcome you
四、对象序列化,实现真正意义的拷贝(数组内的对象也实现深拷贝):
- (void)containerInstanceDeepCopy
{
NSArray *array = [NSArray arrayWithObjects:[NSMutableString stringWithString:@"Welcome"],@"to",@"Xcode",nil];
//数组内对象是指针复制
NSArray *deepCopyArray = [[NSArray alloc] initWithArray:array];
//真正意义上的深复制,数组内对象是对象复制
NSArray *trueDeepCopyArray = [NSKeyedUnarchiver unarchiveObjectWithData:[NSKeyedArchiver archivedDataWithRootObject:array]];
NSLog(@"array: %p", array);
NSLog(@"deepCopyArray: %p", deepCopyArray);
NSLog(@"trueDeepCopyArray: %p", trueDeepCopyArray);
//改变array的第一个元素
[[array objectAtIndex:0] appendString:@" you"];
//只影响deepCopyArray数组的第一个元素
NSLog(@"array[0]: %@", array[0]);
NSLog(@"arrayCopy[0]: %@", deepCopyArray[0]);
//不影响trueDeepCopyArray数组的第一个元素,是真正意义上的深拷贝
NSLog(@"arrayMutableCopy[0]: %@", trueDeepCopyArray[0]);
}
对象序列化:
2018-02-26 19:00:24.289068+0800 深浅拷贝[31729:2753480] array: 0x608000443f60
2018-02-26 19:00:24.289200+0800 深浅拷贝[31729:2753480] deepCopyArray: 0x608000444050
2018-02-26 19:00:24.289296+0800 深浅拷贝[31729:2753480] trueDeepCopyArray: 0x608000443570
2018-02-26 19:00:24.289390+0800 深浅拷贝[31729:2753480] array[0]: Welcome you
2018-02-26 19:00:24.289479+0800 深浅拷贝[31729:2753480] arrayCopy[0]: Welcome you
2018-02-26 19:00:24.289593+0800 深浅拷贝[31729:2753480] arrayMutableCopy[0]: Welcome
五、自定义对象拷贝:
Person.h ------------
@interface Person : NSObject<NSCopying,NSMutableCopying>
@property (nonatomic, strong) NSMutableString *name;//注意!!这里的修饰符不能用copy,否则深拷贝也会返回不可变
@property (nonatomic, copy) NSString *address;
@property (nonatomic, assign) NSInteger age;
@end
Person.m ------------
@implementation Person
- (instancetype)init
{
self = [super init];
if (self) {
self.name = [[NSMutableString alloc]initWithString:@"原name"];
self.address = @"原address";
self.age = 0;
}
return self;
}
//浅拷贝实现- (id)copyWithZone:(NSZone *)zone
-(id)copyWithZone:(NSZone *)zone
{
Person *p = [[self class]allocWithZone:zone];
p.name = [self.name copy];
p.address = [self.address copy];
// p.name = self.name ;
// p.address = self.address ;
p.age = self.age;
return p;
}
//深拷贝实现- (id)mutableCopyWithZone:(NSZone *)zone
-(id)mutableCopyWithZone:(NSZone *)zone
{
Person *p = [[self class] allocWithZone:zone];
p.name = [self.name mutableCopy];
p.address = [self.address mutableCopy];
p.age = self.age;
return p;
}
@end
1、@property (nonatomic, strong) NSMutableString *name;
为什么不用copy修饰?
若用copy修饰这个可变字符串属性,当给name属性赋值时,setter方法会触发深拷贝,导致结果是name属性不再可变,因为可变字符串的copy返回不可变对象。
2、NSString类型的属性用copy
,还是strong
修饰?
@interface TestClass ()
@property (nonatomic, strong) NSString *strongString;
@property (nonatomic, copy) NSString *copyedString;
@end
------
//这种情况下:
NSMutableString *muString = [NSMutableString stringWithFormat:@"abc”];
self.strongString = muString;
self.copyedString = muString;
如果是可变字符串,赋值给strongString时,strongString就直接指向了muString(默认浅拷贝copy),造成万一muString改变,self.strongString就会被改变的bug;
而用copy修饰的self.copyedString,会深拷贝一份新的可变字符串,和muString再无瓜葛~
所以若无上述情况下,strong用也未出错,但是最好用copy,严谨一点。
3.页面传参,传了个可变数组,要注意的地方:
//在页面1跳到页面2,正常传参~
ViewController2 *vc2 = [[ViewController2 alloc]init];
//注意这里需要mutableCopy来深拷贝一个新对象,若不用默认是copy,choiceArray就和页面1传过来的dataArray指向同一个内存,choiceArray改变的话,就会改变页面1的dataArray数组,造成数据错乱的莫名bug~
vc2.choiceArray = [self.dataArray mutableCopy];
[self.navigationController pushViewController:vc2 animated:YES];
孰能生巧,复习整理一下小知识点^_^