iOS面试专题

iOS 中的深拷贝与浅拷贝

2018-08-17  本文已影响0人  苏东没有坡

前言

一. copy 与 mutableCopy 方法

要注意的是,我们并不能单纯地把copymutableCopy认为是浅拷贝深拷贝

iOS 对象大致可以分为容器对象非容器对象,细分下去,可以分为 可变容器对象(NSMutableArray),不可变容器对象(NSArray) ,可变非容器对象(NSMutableString,NSMutableDictionary),不可变非容器对象(NSString,NSDictionary)

下面我们来实际验证一下copy与mutableCopy的效果

  1. 非容器不可变对象
  NSString *str1 = @"非容器不可变对象";
  NSString *str2 = [str1 copy];
  NSString *str3 = [str1 mutableCopy];
        
  NSLog(@"str1:%p class:%@",str1,[str1 class]);
  NSLog(@"str2:%p class:%@",str2,[str2 class]);
  NSLog(@"str3:%p class:%@",str3,[str3 class]);

 //  打印结果
  str1:0x105718738     class:__NSCFConstantString
  str2:0x105718738     class:__NSCFConstantString
  str3:0x60400024eb80  class:__NSCFString
  

结论:对于非容器不可变对象来说, copy浅拷贝mutableCopy深拷贝

  1. 非容器可变对象
 NSMutableString *str1  = [NSMutableString stringWithFormat:@"非容器可变对象"];
 NSMutableString *str2 = [str1 copy];
 NSMutableString *str3 = [str1 mutableCopy];
        
 NSLog(@"str1:%p class:%@",str1,[str1 class]);
 NSLog(@"str2:%p class:%@",str2,[str2 class]);
 NSLog(@"str3:%p class:%@",str3,[str3 class]);
 
 // 打印结果
 str1:0x600000251be0 class:__NSCFString
 str2:0x600000251010 class:__NSCFString
 str3:0x600000251c40 class:__NSCFString

结论:对于非容器可变对象来说,copy,mutableCopy都是深拷贝

这个结论其实解释了NSString对象建议用copy而不是strong关键字修饰的问题-数据安全问题。详细见下面。

  1. 容器不可变对象
NSArray *array1 = [NSArray arrayWithObjects:@"非容器不可变对象",[NSMutableString stringWithFormat:@"非容器可变对象"], nil];
NSArray *array2 = [array1 copy];
NSArray *array3 = [array1 mutableCopy];
        
NSLog(@"array:%p  copyArray:%p  mutableCopyArray:%p",array1,array2,array3);
NSLog(@"array1[0]:%p   array1[1]:%p  ",array1[0] , array1[1]);
NSLog(@"array2[0]:%p   array2[1]:%p  ",array2[0] , array2[1]);
NSLog(@"array3[0]:%p   array3[1]:%p  ",array3[0] , array3[1]);

 //  打印结果
array:0x6040002312a0  copyArray:0x6040002312a0  mutableCopyArray:0x6040002544f0
array1[0]:0x105718738   array1[1]:0x60400024eb80
array2[0]:0x105718738   array2[1]:0x60400024eb80
array3[0]:0x105718738   array3[1]:0x60400024eb80

结论: 1. 对于容器不可变对象来说, copy浅拷贝, mutableCopy深拷贝
2. 对于容器内的元素来说,copymutableCopy都是浅拷贝

  1. 容器可变对象
NSMutableArray *array1 = [NSMutableArray arrayWithObjects:@"非容器不可变对象",[NSMutableString stringWithFormat:@"非容器可变对象"], nil];
NSMutableArray *array2 = [array1 copy];
NSMutableArray *array3 = [array1 mutableCopy];
        
NSLog(@"array1:%p  array2:%p  array3:%p",array1,array2,array3);
NSLog(@"array1[0]:%p   array1[1]:%p ",array1[0], array1[1]);
NSLog(@"array2[0]:%p   array2[1]:%p ",array2[0], array2[1]);
NSLog(@"array3[0]:%p   array3[1]:%p ",array3[0], array3[1]);

// 打印结果
array1:0x604000254820  array2:0x604000231a00  array3:0x6040002545b0
array1[0]:0x105718738   array1[1]:0x6040002544f0
array2[0]:0x105718738   array2[1]:0x6040002544f0
array3[0]:0x105718738   array3[1]:0x6040002544f0

结论:1. 对于容器可变对象来说,copy,mutableCopy都是深拷贝
2.对于容器内的元素来说,copymutableCopy都是浅拷贝

  1. copy 与mutableCopy 后得到的对象
NSString *str1 = @"非容器不可变对象";
NSMutableString *str2 = [str1 mutableCopy];
[str2 appendString:@"111"];
NSLog(@"str2:%@",str2);

// 打印结果
str2:非容器不可变对象111

/******************************************************************************/

NSMutableString *str1 = [NSMutableString stringWithFormat:@"非容器可变对象"];
NSMutableString *str2 = [str1 copy];
[str2 appendString:@"111"];

// 执行[str2 appendString:@"111"]报错
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Attempt to mutate immutable object with appendString:'

结论:1. 不论被拷贝对象是否可变,执行了mutableCopy之后得到的对象都是可变的
2.不论被拷贝对象是否可变,执行了copy之后得到的对象都不可变

  1. 自定义对象
@interface Singer() <NSCopying>
@property (nonatomic, strong) NSString *name;
@end

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

二. NSString 对象copy和strong关键字修饰的区别

NSString *str = @"我是个不可变字符串";
self.strongStr = str;
self.copyedStr = str;
       
NSLog(@"str:%p, strongStr:%p, copyedStr:%p",str,self.strongStr,self.copyedStr);

// 打印结果
str:0x10cd90978, strongStr:0x10cd90978, copyedStr:0x10cd90978

当字符串是不可变类型时,self.strongStrself.copyedStr 并无区别,指向的都是str的内存地址。

NSMutableString *str = [NSMutableString stringWithFormat:@"我是个可变字符串"];
self.strongStr = str;
self.copyedStr = str;
[str appendString:@"哈哈哈"];

NSLog(@"str:%p, strongStr:%p, copyedStr:%p",str,self.strongStr,self.copyedStr);
NSLog(@"str:%@, strongStr:%@, copyedStr:%@",str,self.strongStr,self.copyedStr);

// 打印结果
str:0x6000002507d0, strongStr:0x6000002507d0, copyedStr:0x600000251760
str:我是个可变字符串哈哈哈, strongStr:我是个可变字符串哈哈哈, copyedStr:我是个可变字符串

当字符串是可变类型时,self.copyedStr指向了新的内存地址,self.copyedStr指向的依旧是str的内存地址。

strong关键字修饰NSString类型的属性究竟会造成什么问题呢?
当我们对strongStr属性赋值完毕之后,对str进行修改,由于指向的是同一个内存地址,self. strongStr的值也发生了变化。strongStr在我们不知道的情况下发生了修改行为,这是不安全的。所以在类型不明确的情况下,一般建议使用copy关键字修饰NSString类型的属性。

上一篇下一篇

猜你喜欢

热点阅读