对于不可变的集合类对象进行 copy 操作,只是改变了指针,其内存地址并没有发生变化;进行 mutableCopy 操作,内存地址发生了变化,但是其中的元素内存地址并没有发生变化。
对于可变集合类对象,不管是进行 copy 操作还是 mutableCopy 操作,其内存地址都发生了变化,但是其中的元素内存地址都没有发生变化,属于单层深拷贝。
深拷贝的实现
- 第一种方法,可以通过归解档生成两份完全独立的对象,但是前提是对象必须支持 NSCoding 协议。
- 第二种方法,自己实现了一个 BNDeepCopy 深拷贝协议,把 NSArray、NSSet、NSDictionary 分别用 category 添加一下实现。后面如果自己的某个对象如果 NSCopying 协议不能满足深拷贝的要求,只需实现 BNDeepCopy 协议即可。(对于一些 NSString、NSNumber 的内存优化,此实现中暂时不独立成两份)。
大概实现如下:
@implementation NSArray (BNDeepCopy)
- (instancetype)BN_deepCopy {
NSMutableArray *mutableResultArray = [[NSMutableArray alloc] initWithCapacity:[self count]];
for (id subObject in self) {
id deepCopySubObject = nil;
if ([subObject respondsToSelector:@selector(BN_deepCopy)]) {
deepCopySubObject = [subObject BN_deepCopy];
} else if ([subObject isKindOfClass:[NSMutableArray class]] || [subObject isKindOfClass:[NSMutableSet class]] || [subObject isKindOfClass:[NSMutableDictionary class]]) {
deepCopySubObject = [subObject mutableCopy];
} else if ([subObject conformsToProtocol:@protocol(NSCopying)]) {
deepCopySubObject = [subObject copy];
} else {
deepCopySubObject = subObject;
}
if (deepCopySubObject) {
[mutableResultArray addObject:deepCopySubObject];
} else {
[mutableResultArray addObject:subObject];
}
}
if ([self isKindOfClass:[NSMutableArray class]]) {
return mutableResultArray;
} else {
return [NSArray arrayWithArray:mutableResultArray];
}
}
浅拷贝和深拷贝都是说引用数据类型,不是说基本数据类型
浅拷贝是说在给一个变量赋值的是将另一个变量的引用(内存地址)赋值给新变量,而不是又重新造了一个新的东西来
深拷贝是将重新制造出一个变量的副本,然后将变量的副本赋值给新变量
浅拷贝:指针拷贝,不产生新的对象,源对象的引用计数器+1;
深拷贝:对象拷贝,会产生新的对象,源对象的引用计数器不变;
判断是浅拷贝和深拷贝就看一下两个变量的内存地址是否一样,一样就是浅拷贝,不一样就是深拷贝,也可以改变一个变量的其中一个属性值看两者的值都会发生变化;
copy
拷贝的结果是一个不可变(imutable)的对象, 无论源对象是可变的还是不可变的,copy之后的都是不可变的类型
不可变类型 变量名 = [不可变类型|可变类型 copy];
mutableCopy
可变拷贝的结果的数据类型是一个可变的对象,无论源对象时不可变的还是可变的,可变拷贝之后的数据类型都是可变类型
可变类型 变量名 = [不可变类型|可变类型 mutableCopy];
copy对引用计数器的影响
拷贝一个不可变的类型的结果是新对象和源对象都指向同一个内存地址,即使指针拷贝,属于浅拷贝,所以不生产新对象,源对象的引用计数+1
拷贝一个可变的类型,会生成一个新对象,不影响源对象的引用计数
mutableCopy对引用计数器的影响:
无论对可变类型或者对不可变类型使用mutableCopy操作,都不会影响源对象的引用计数
除了对一个不可变的类型进行拷贝操作外会对源对象的引用计数+1,其他情况(无论是对可变类型或不可变类型 进行拷贝或者可变拷贝)都会生成一个全新的对象(对象的引用计数器从0到1),都不会影响源对象的引用计数。**
copy一般不会对源对象的引用计数+1,即使对自定义的对象(实现了NSCopying协议)进行copy也不会影响源对象的引用计数,除了 [不可变类型 copy]例外,因为这种情况特殊,因为:对一个不可变类型的对象copy之后也是不可变的类型,既然不可变也没法修改,再生成一份新的对象感觉在内存上有些浪费,还不如对源对象的引用计数+1,这样既达到节约内存的目的,也不破坏内存管理规则。copy会是新对象的引用计数器值为1**
重要的事情所三遍,再一次强调一下一种特殊情况,对 不可变类型 copy操作会,是一种指针拷贝,是浅拷贝,源对象和新对象会指向同一块内存地址,copy之后会是引用计数器+1
测试代码:
#import <Foundation/Foundation.h>
#import "User.h"
// copy 不可变的对象,OC对该类型的操作进行了特殊优化
// copy 会得到一个全新的对象,新的内存地址,但是对于[NSArray copy]操作,并没有这样,这是因为系统对此进行了优化,减少内存的开销,但这样并不影响内存管理规则的使用,不用开辟新内存,只要要增加一个引用计数即可,注意系统只是对不可变类型 的 不可变复制 进行了优化,是的copy能使得引用计数+1,但并不使用与其他情况,包括自定义对象这种情况,也不能得出copy能使引用计数+1,内存法则中是说如果对一个对象copy,那么新的对象的计数将为1,是说的新对象,而不是源对象, 所以说copy并不能使源对象应用计数+1(不包括[NSArray copy]这种情况), 只能将新对象的引用计数置为1
void copyNotMutableType() {
// 不可变数组
NSArray *array = [NSArray arrayWithObjects:@"1", @"2", @"3", nil];
NSLog(@"【array】 retainCount:%ld", [array retainCount]); // 1
NSArray *array2 = [array copy];
NSLog(@"【array】 retainCount:%ld", [array retainCount]); // 2
// 难道copy也把对象的引用数量也拷贝了, retainCount也是该对象的一个实例变量,拷贝整个对象当然也会复制了该变量,所以和array 的retainCount的数一样
NSLog(@"《array2》 retainCount:%ld", [array2 retainCount]); // 2
[array2 retain];
NSLog(@"《array2》 retainCount:%ld", [array2 retainCount]); // 3
NSLog(@"【array】 retainCount:%ld", [array retainCount]); // 3
if (array == array2) {
NSLog(@"同一个对象");
}
NSLog(@"【array】:%@", array); // [1, 2, 3]
NSLog(@"《array2》:%@", array2); // [1, 2, 3]
[array2 release];
[array2 release];
[array release];
}
// 对可变类型copy,不影响可变变量的引用计数
void copyMutableType() {
NSMutableArray *mutableArray = [NSMutableArray arrayWithObjects:@"1", @"2", @"3", nil];
NSLog(@"【mutableArray】 retainCount:%ld", [mutableArray retainCount]); // 1
[mutableArray retain];
NSLog(@"【mutableArray】 retainCount:%ld", [mutableArray retainCount]); // 2
NSArray *array = [mutableArray copy];
NSLog(@"【mutableArray】 retainCount:%ld", [mutableArray retainCount]); // 2:对可变类型copy不会影响引用计数器
NSLog(@"【array】 retainCount:%ld", [array retainCount]); // 1
[mutableArray addObject:@"4"];
NSLog(@"mutableArray:%@", mutableArray); // [1, 2, 3, 4]
NSLog(@"array:%@", array); // [1, 2, 3]
}
// mutableCopy 可变拷贝一个非可变类型的对象 不会影响源对象的引用计数
void mutableCopyNotMutableType() {
NSArray *array = [NSArray arrayWithObjects:@"1", @"2", @"3", nil];
NSLog(@"【array】 retainCount:%ld", [array retainCount]); // 1
[array retain];
NSLog(@"【array】 retainCount:%ld", [array retainCount]); // 2
NSMutableArray *mutableArray = [array mutableCopy];
NSLog(@"【array】 retainCount:%ld", [array retainCount]); // 2
NSLog(@"《mutableArray》 retainCount:%ld", [mutableArray retainCount]); // 1
[mutableArray addObject:@"4"];
NSLog(@"mutableArray:%@", mutableArray); // [1, 2, 3, 4]
NSLog(@"array:%@", array); // [1, 2, 3]
}
//
void mutableCopyMutableType(){
NSMutableArray *mutableArray = [NSMutableArray arrayWithObjects:@"1", @"2", @"3", nil];
NSLog(@"【mutableArray】 retainCount:%ld", [mutableArray retainCount]); // 1
[mutableArray retain];
NSLog(@"【mutableArray】 retainCount:%ld", [mutableArray retainCount]); // 2
NSMutableArray *mutableCopyArray = [mutableArray mutableCopy];
NSLog(@"【mutableArray】 retainCount:%ld", [mutableArray retainCount]); // 2
NSLog(@"《mutableCopyArray》retainCount:%ld", [mutableCopyArray retainCount]); // 1
[mutableArray addObject:@"4"];
NSLog(@"mutableArray:%@", mutableArray); // [1, 2, 3, 4]
NSLog(@"mutableCopyArray:%@", mutableCopyArray); // [1, 2, 3]
}
void test(){
NSArray *array = [NSArray arrayWithObjects:@"1", @"2", @"3", nil];
NSLog(@"【array】 retainCount:%ld", [array retainCount]);
[array copy];
NSLog(@"【array】 retainCount:%ld", [array retainCount]);
}
void testObject(){
User *user = [[User alloc] init];
user.username = @"zhangsan";
user.age = 25;
NSLog(@"【user】 retainCount:%ld", [user retainCount]);
// copy 自定义对象难到引用计数不+1?
User *copyUser = [user copy];
NSLog(@"【[user]】 retainCount:%ld", [user retainCount]);
NSLog(@"【copyUser】 retainCount:%ld", [copyUser retainCount]);
copyUser.username = @"lisi";
NSLog(@"copyUser:%@", copyUser);
NSLog(@"user:%@", user);
[copyUser release];// 遵守内存管理规则
[user release];
}
int main(int argc, const char * argv[]) {
// Person *xiaohong = [[Person alloc] init];
// NSString *name = @"xiaohong";
// [xiaohong setName:name];
// [xiaohong setName:name];
// [xiaohong setName:name];
// NSLog(@"4 retainCount : %ld", [[xiaohong name] retainCount]);
copyNotMutableType();
copyMutableType();
mutableCopyNotMutableType();
mutableCopyMutableType();
test();
testObject();
return 0;
}
#import <Foundation/Foundation.h>
@interface User : NSObject <NSCopying>
@property (copy, nonatomic) NSString *username;
@property (assign, nonatomic) int *age;
@end
#import "User.h"
@implementation User
- (id)init {
if (self = [super init]) {
}
return self;
}
- (id)copyWithZone:(nullable NSZone *)zone {
User *copyUser = [[self class] allocWithZone:zone];
copyUser.username = self.username;
copyUser.age = self.age;
return copyUser;
}
@end