什麼是淺複製(淺拷貝)與深複製(深拷貝)?
淺拷貝是指源對象與拷貝對象共用一份實體,僅僅是引用變量不同,即名稱不同。對某中任何一個對象的改動都會影響另一個對象。深拷貝是指源對象與拷貝對象互相獨立,其中任何一個對象的改動都不會對另一個對象造成影響。
淺拷貝與深拷貝的區別?
1、淺拷貝是指將對象中的數值類型的字段拷貝到新對象中,而對象中的引用型字段則只複製它的一個引用到上標對象。如果改變目標對象中的引用型字段的值則將反映在原對象中,也就是說原始對象中對象的字段也會發生變化。
2、深拷貝與淺拷貝不同的是對於引用的處理,深拷貝將會在新對象中創建一個新的和原對象中對應字段內容相同的字段,也就是說這個引用對象與原有對象引用是不同的,在改變新對象中這個字段的進修是不會影響到原始對象中對應字段的內容。
OC中淺拷貝與深拷貝的區別?
淺拷貝
1、複製時,源對象在複製時引用計數器+1,相當於做一個retain操作,但沒有產生新的對象
2、複製後,源對象和副本對象是同一個對象。
深拷貝
1、複製時,源對象引用計數不變,副本對象引用計數爲1,產生了新的對象
2、複製後,源對象和副本對象是兩個不同的對象。
retain、copy和MutableCopy的區別
1、retain:始終是淺複製。引用計數每次加一。返回對象是否可變與被複制的對象保持一致。2、copy:對於可變對象爲深複製,引用計數不改變;對於不可變對象是淺複製, 引用計數每次加一。始終返回一個不可變對象。
3、mutableCopy:始終是深複製,引用計數不改變。始終返回一個可變對象。
容器類與非容器類
1、非容器類,NSString NSInteger NSNumber NSArray, NSSet, NSDictionary等,即不包含子類型的數據類型。2、容器類,就是自定義的組合類,即有多個數據類型的組合而成。
非容器類,基本數據類型的拷貝是淺拷貝
對於那些實現了NSCopying,NSMutableCopying協議的基本類型,如NSString、NSNumber、NSDictionary、NSArray、NSSet等,這些在執行copy mutableCopy時是淺拷貝。
// 數字
NSNumber *n1 = @(1);
NSNumber *n2 = [n1 copy];
NSLog(@"n1-%p, n2-%p", n1, n2);
// 字符串
NSString *s1 = @"s1";
NSString *s2 = [s1 copy];
NSLog(@"s1-%p, s2-%p", s1, s2);
// 數組
NSArray *a1 = @[@"1", @"2", @"3"];
NSArray *a2 = [a1 copy];
NSLog(@"a1-%p, a2-%p", a1, a2);
// 字典
NSDictionary *d1 = @{@"k1":@"v1", @"k2":@"v2"};
NSDictionary *d2 = [d1 copy];
NSLog(@"d1-%p, d2-%p", d1, d2);
// 集合
NSSet *t1 = [[NSSet alloc]initWithArray:@[@"1", @"2"]];
NSSet *t2 = [t1 copy];
NSLog(@"t1-%p, t2-%p", t1, t2);
執行結果爲,
n1-0xb000000000000012, n2-0xb000000000000012
s1-0x1073ed098, s2-0x1073ed098
a1-0x618000052a20, a2-0x618000052a20
d1-0x610000075d00, d2-0x610000075d00
t1-0x618000052990, t2-0x618000052990
自定義類型,即容器類型的深淺拷貝
1、只包含非容器類型的容器類
自定義一個學生,只有一個NSString的name屬性時,我們僅以實現NSCopy協議爲例
student.h
/**
* @file Student.h
* @brief 學生實體類
* @author ruglcc
* @version 1.0
* @date 2017/03/29
*/
#import <Foundation/Foundation.h>
@interface Student : NSObject <NSCopying>
/// 姓名
@property (nonatomic, strong) NSString *name;
- (instancetype)initWithName:(NSString *)name;
@end
student.m
/**
* @file Student.m
* @brief 學生實體類
* @author ruglcc
* @version 1.0
* @date 2017/03/29
*/
#import "Student.h"
@implementation Student
- (instancetype)initWithName:(NSString *)name
{
self = [super init];
if(self)
{
_name = name;
}
return self;
}
- (id)copyWithZone:(nullable NSZone *)zone
{
Student *student = [[self class]allocWithZone:zone];
// 淺拷貝
//student.name = _name;
// 深拷貝
student.name = [_name copy];
return student;
}
@end
測試函數
// 學生小李
Student *xiaoLi = [[Student alloc]initWithName:@"小李"];
// 通過小李克隆一個小王
Student *xiaoWang = [xiaoLi copy];
NSLog(@"xiaoLi: %p, xiaoWang : %p", xiaoLi, xiaoWang);
NSLog(@"xiaoLi.name: %p, xiaoWang.name: %p", xiaoLi.name, xiaoWang.name);
測試結果
2017-03-29 15:16:54.276 CopyDemo[59192:14737328] xiaoLi:0x60000001ed60, xiaoWang : 0x60000001ee80
2017-03-29 15:16:54.276 CopyDemo[59192:14737328] xiaoLi.name: 0x10875a218, xiaoWang.name: 0x10875a218
2、 嵌套類型的淺拷貝
現在手機人手一部,學生也不例外了,我們向學生類中添加一個新屬性:手機。手機類簡單定義爲:
phone.h
/**
* @file Phone.h
* @brief 手機實體類
* @author ruglcc
* @version 1.0
* @date 2017/03/29
*/
#import <Foundation/Foundation.h>
@interface Phone : NSObject<NSCopying>
/// 手機品牌或型號
@property (nonatomic, strong) NSString *name;
/// 電話號碼
@property (nonatomic, strong) NSString *number;
- (instancetype)initWithName:(NSString *)name
number:(NSString *)number;
@end
/**
* @file Phone.m
* @brief 手機類實現
* @author ruglcc
* @version 1.0
* @date 2017/03/29
*/
#import "Phone.h"
@implementation Phone
- (instancetype)initWithName:(NSString *)name
number:(NSString *)number
{
self = [super init];
if(self)
{
_name = name;
_number = number;
}
return self;
}
- (id)copyWithZone:(nullable NSZone *)zone
{
Phone *phone = [[self class]allocWithZone:zone];
// 對於基本數據類型,下面兩種方式是一樣的, 因爲基本數據類型執行copy也是淺拷貝。
// 1、淺拷貝
//phone.name = _name;
//phone.number = _number;
// 2、深拷貝
phone.name = [_name copy];
phone.number = [_number copy];
return phone;
}
@end
添加手機屬性後的學生類
student.h
#import "Phone.h"
@interface Student : NSObject <NSCopying>
/// 姓名
@property (nonatomic, strong) NSString *name;
@property (nonatomic, strong) Phone *phone;
- (instancetype)initWithName:(NSString *)name phone:(Phone*)phone;
@end
student.m
#import "Student.h"
@implementation Student
- (instancetype)initWithName:(NSString *)name phone:(Phone*)phone
{
self = [super init];
if(self)
{
_phone = phone;
_name = name;
}
return self;
}
- (id)copyWithZone:(nullable NSZone *)zone
{
Student *student = [[self class]allocWithZone:zone];
// 淺拷貝
student.name = _name;
student.phone = _phone;
return student;
}
@end
測試函數
// 定義一個手機
Phone *phoneXl = [[Phone alloc]initWithName:@"iphone7" number:@"13888888888"];
// 學生小李
Student *xiaoLi = [[Student alloc]initWithName:@"小李" phone:phoneXl];
// 通過小李克隆一個小王
Student *xiaoWang = [xiaoLi copy];
NSLog(@"xiaoLi: %p, xiaoWang : %p", xiaoLi, xiaoWang);
NSLog(@"xiaoLi.name: %p, xiaoWang.name: %p", xiaoLi.name, xiaoWang.name);
NSLog(@"xiaoLi.phone: %p, xiaoWang.phone: %p", xiaoLi.phone, xiaoWang.phone);
xiaoWang.phone.name = @"榮譽7";
NSLog(@"xiaoLi.phone %@", xiaoLi.phone.name);
測試結果
CopyDemo[59477:14770601] xiaoLi: 0x61800003c3a0, xiaoWang : 0x61800003c500
CopyDemo[59477:14770601] xiaoLi.name: 0x10c7dd258, xiaoWang.name: 0x10c7dd258
CopyDemo[59477:14770601] xiaoLi.phone: 0x61800003c5e0, xiaoWang.phone: 0x61800003c5e0
CopyDemo[59477:14770601] xiaoLi.phone 榮譽7
結論:
1、源對象與拷貝對象的地址不同,說明淺拷貝生成了新的容器對象。
2、源對象與拷貝對象的子自定義對象的地址相同,說明淺拷貝雖然生成了新的容器對象,但是它們包含的內容是同一個對象。
3、修改拷貝對象的子自定義對象的值,對應的源對象的屬性值也改變了,也進一步說明,淺拷貝時,子自定義對象沒有產生新對象。
3、嵌套類型的深拷貝
同樣對於上例,將學生類中 - (id)copyWithZone:(nullable NSZone *)zone 方法實現改爲 深拷貝方式
#import "Student.h"
@implementation Student
- (instancetype)initWithName:(NSString *)name phone:(Phone*)phone
{
self = [super init];
if(self)
{
_phone = phone;
_name = name;
}
return self;
}
- (id)copyWithZone:(nullable NSZone *)zone
{
Student *student = [[self class]allocWithZone:zone];
// 淺拷貝
// student.name = _name;
//student.phone = _phone;
// 深拷貝
student.name = [_name copy];
student.phone = [_phone copy];
return student;
}
@end
同樣的測試函數
// 定義一個手機
Phone *phoneXl = [[Phone alloc]initWithName:@"iphone7" number:@"13888888888"];
// 學生小李
Student *xiaoLi = [[Student alloc]initWithName:@"小李" phone:phoneXl];
// 通過小李克隆一個小王
Student *xiaoWang = [xiaoLi copy];
NSLog(@"xiaoLi: %p, xiaoWang : %p", xiaoLi, xiaoWang);
NSLog(@"xiaoLi.name: %p, xiaoWang.name: %p", xiaoLi.name, xiaoWang.name);
NSLog(@"xiaoLi.phone: %p, xiaoWang.phone: %p", xiaoLi.phone, xiaoWang.phone);
xiaoWang.phone.name = @"榮譽7";
NSLog(@"xiaoLi.phone %@", xiaoLi.phone.name);
測試結果
2017-03-29 15:45:53.052 CopyDemo[59549:14775946] xiaoLi: 0x60000002da80, xiaoWang : 0x600000031340
2017-03-29 15:45:53.054 CopyDemo[59549:14775946] xiaoLi.name: 0x10459a258, xiaoWang.name: 0x10459a258
2017-03-29 15:45:53.054 CopyDemo[59549:14775946] xiaoLi.phone: 0x600000030c00, xiaoWang.phone: 0x600000030680
2017-03-29 15:45:53.055 CopyDemo[59549:14775946] xiaoLi.phone iphone7
結論:
1、源對象與拷貝對象的地址不同,說明自定義對象引用本身深拷貝。
2、源對象與拷貝對象中的子屬性的地址都不同了,說明深拷貝是完全不同的兩個對象了
3、修改拷貝對象中自定義對象中的一個子屬性,源對象對應的屬性沒有變。說明深拷貝是生成了兩個不同的對象。