深拷贝和浅拷贝

对象拷贝有两种方式:浅复制和深复制。顾名思义,浅复制,并不拷贝对象本身,仅仅是拷贝指向对象的指针;深复制是直接拷贝整个对象内存到另一块内存中

浅复制(shallow copy):在浅复制操作时,对于被复制对象的每一层都是指针复制。
深复制(one-level-deep copy):在深复制操作时,对于被复制对象,至少有一层是深复制。
完全复制(real-deep copy):在完全复制操作时,对于被复制对象的每一层都是对象复制。

在非集合类对象中:对immutable对象进行copy操作,是指针复制,mutableCopy操作时内容复制;对mutable对象进行copy和mutableCopy都是内容复制。用代码简单表示如下:

[immutableObject copy] // 浅复制
[immutableObject mutableCopy] //深复制
[mutableObject copy] //深复制
[mutableObject mutableCopy] //深复制

在集合类对象中,对immutable对象进行copy,是指针复制,mutableCopy是内容复制;对mutable对象进行copy和mutableCopy都是内容复制。但是:集合对象的内容复制仅限于对象本身,对象元素仍然是指针复制。用代码简单表示如下:

[immutableObject copy] // 浅复制
[immutableObject mutableCopy] //单层深复制
[mutableObject copy] //单层深复制
[mutableObject mutableCopy] //单层深复制

copy:创建的是不可变副本(NSString,NSArray,NSDictionary)
mutableCopy:创建的是可变副本(NSMutableString,NSMutableArray,NSMutableDictionary)

容器类的深拷贝实现

  • 可以通过归解档生成两份完全独立的对象,但是前提是对象必须支持 NSCoding
为什么字符串要用copy

上代码解释,先用strong试试


@interface ViewController ()

@property(nonatomic,strong)NSString*whyStringUserCopyStr;

@end


@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
}


-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    
    [self whyStringUserCopy];
}

-(void)whyStringUserCopy{
    
    NSMutableString *string = [NSMutableString string];
    
    [string appendString:@"hello"];
    
    self.whyStringUserCopyStr = string;
    
    NSLog(@"%@",self.whyStringUserCopyStr);
    NSLog(@"string =%p %p whyStringUserCopyStr=%p %p",string,&string,_whyStringUserCopyStr,&_whyStringUserCopyStr);
    [string appendString:@"World"];
    
    NSLog(@"%@",self.whyStringUserCopyStr);

}

打印出的信息

 hello
 string =0x600000245b50 0x7ffee35f92e8 whyStringUserCopyStr=0x600000245b50 0x7f9db3607800
 helloWorld

你会发现self.whyStringUserCopyStr只被赋值了一次,却改变了两次
在看中间打印出string的本身的地址和string指向的地址 以及 whyStringUserCopyStr的本身的地址和whyStringUserCopyStr指向的地址

strong相当于whyStringUserCopyStr指向了可变字符串的地址,所以可变字符串改变了那么whyStringUserCopyStr也会跟着改变,如果用copy将可变字符串重新拷贝一份,重新开辟内存空间,修改可变字符串的值,不会对whyStringUserCopyStr造成影响
因为可变对象的copy是深拷贝

刚刚的演示,我使用了NSMutableString(可变字符串),对mutableString执行copy操作,属于深拷贝,所以开辟的新内存空间,如果使用的是NSString(不可变内存),对NSString进行copy属于浅拷贝,不会开辟新的内存空间,是不是就不会出现这个问题了呢?

接下来使用NSString来进行演示,将属性修饰符再次改回strong

#import "ViewController.h"

@interface ViewController ()

/*    为什么字符串使用copy修饰?
    这里先不使用copy修饰,NSString是OC对象,并且是ARC模式,所以先使用strong来修饰演示
 
 */
@property (nonatomic,strong) NSString *name;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];

    NSString *str = [NSString string];
    str = @"xiaoming";
    
    self.name = str;
    NSLog(@"str:%p,name:%p",str,_name);
    
    str = @"xiaogang";
    NSLog(@"str:%p,name:%p",str,_name);
    
    NSLog(@"%@",_name);
}

@end


打印

str:0x10c937060,name:0x10c937060
str:0x10c9370a0,name:0x10c937060
xiaoming

从结果上看,重新设置str的值后,self.name确实没有受到影响

原因:
将之前的可变字符串变为不可变字符串,因为NSString不支持append添加操作,我这里的两次str赋值操作,其实是让str重新指向了一片内存空间,并不是修改了str原本内存中的值

OC中对象即指针,实际上存储的是内存地址,self.name = str;
实际是将str存储的@"xiaoming"这块地址给了self.name

self.name还指向着@"xiaoming"
所以改变str的指向后,self.name的指向并没有改变,输出没有受到影响

;