文章目录
1.深拷贝和浅拷贝概念
-
由上面的图我们可以明确地看出:
-
浅拷贝(Shallow copy)
:是指针复制,它们指向共同的内存地址,没有开辟新的空间,当内存销毁的时候,指向这片内存的几个指针需要重新定义才可以使用,要不然会成为野指针。相当于对象做一次retain操作,引用计数加1。 -
深拷贝(Deep copy)
:是指内容拷贝,拷贝后的对象会分配新的内存空间,两个对象虽然存的值是相同的,但是内存地址不一样,两个对象也互不影响,互不干涉。源对象的引用计数不变,副本对象的引用计数为1。
3.
区别总结
:浅拷贝就是指针拷贝,深拷贝就是内容拷贝;质区别在于是否开启新的内存空间、是否影响内存地址的引用计数。 -
2.copy和mutableCopy
copy
拷贝出来的对象类型总是不可变类型,mutableCopy
拷贝出来的对象类型总是可变类型(下面示1例可以证明)。
3.OC中的非集合类对象和集合类对象的深浅拷贝
一. 非集合类对象:NSString,NSMutableString,NSData,NSNumber
非集合对象的copy
与mutableCopy
示例1:
NSString *str1 = @"字符串1";
NSString *str2 = [str1 copy];
NSMutableString *str3 = [str1 mutableCopy];
NSMutableString * str4 = [NSMutableString stringWithString:@"字符串2"];
NSString * str5 = [str4 copy];
NSMutableString *str6 = [str4 mutableCopy];
打断点lldb调试:
- 在此解释一下在
runtime
下NSString
的“真身”是__NSCFConstantString
而NSMutableString
的“真身”是__NSCFString
,然后我们就能很清楚的看到,只要是copy
得到的值就是不可变类型,而mutablecopy
得到的是可变类型。但是str5
的类型显示是__NSCFString
类型,应该是__NSCFConstantString
类型才对啊,这里是为什么呢?个人猜测是子类指针指向父类(继承多太的一种体现),其本质还是父类,调NSMutableString
的特有方法如运行[str5 appendFormat:@"sss"]
的时候会crash,原因就是str5
本质是NSString
类型不能调用子类NSMutableString
特有的方法。 - 结果分析:
1.对不可变非集合对象进行 copy ,属于浅拷贝,不产生新的对象。
2.对不可变非集合对象 进行 mutableCopy ,属于深拷贝,产生新的可变对象。
3.对 可变非集合对象 进行 copy, 属于深拷贝,产生新的不可变对象。
4.对可变非集合对象进行mutableCopy,属于深拷贝, 产生新的可变对象。
说明
:上面所说的对象其实不大合适,对象本质就是指针,上面所说的对象其实是指分配的内存,便于理解。
二.集合类对象:NSArray,NSMutableArray,NSDictionary,NSSet...
集合对象的copy
与mutableCopy
示例2:
NSArray * arr1 = [NSArray arrayWithObjects:@1,@2,@3,nil];
NSArray * arr2 = [arr1 copy];
NSMutableArray * arr3 = [arr1 mutableCopy];
NSMutableArray * arr4 = [NSMutableArray arrayWithObjects:@4,@5,@6,nil];
NSArray * arr5 = arr4.copy;
NSMutableArray * arr6 = arr4.mutableCopy;
打断点lldb调试:
- 结果分析:
1.对不可变集合对象进行 copy ,属于浅拷贝,不产生新的对象。
2.对不可变集合对象 进行 mutableCopy ,属于单层深拷贝,产生新的可变对象。
3.对 可变集合对象 进行 copy, 属于单层深拷贝,产生新的不可变对象。
4.对可变集合对象进行mutableCopy,属于单层深拷贝, 产生新的可变对象。
单层深拷贝:
是指集合对象的内容复制仅限于对象本身,对象元素仍然是指针复制,列如上面的arr3
是[arr1 mutableCopy]
产生的,产生的arr3
是可变的数组,arr3里面的NSNumber
元素只是指针复制并没有进行深复制。 - 不管是集合对象还是非集合对象都遵从以下规律:
源对象类型 | 拷贝方式 | 目标对象类型 | 是否开辟新内存 | 拷贝类型 |
---|---|---|---|---|
不可变对象 | copy | 不可变对象 | 否 | 浅拷贝(指针拷贝) |
不可变对象 | mutableCopy | 可变对象 | 是 | 深拷贝(内容拷贝) |
可变对象 | copy | 不可变对象 | 是 | 深拷贝(内容拷贝) |
可变对象 | mutableCopy | 可变对象 | 是 | 深拷贝(内容拷贝) |
4.property中copy关键字
- 当我们使用一个
copy
关键字声明一个对象的时候, 调用setter
方法的时候,copy
关键字会为对象自动copy
一个副本。
示例3:
@property (nonatomic, copy) NSArray *array;
- (void)setArray:(NSArray *)array {
_array = [array copy];
}
此时不管传进来的array
是NSArray
还是NSMutableArray
类型都会产生一个array copy 了一个副本,是不可变的。
如果我们直接用strong关键字的话,又是怎样的呢?
示例4:
@property (nonatomic, strong) NSArray *array;
- (void)setArray:(NSArray *)array {
_array = array;
}
如果此时传入的array
是一个NSMutableArray
的话,self.array
可能会在不知情的情况下被修改。
- 为什么用
@property
声明的NSString
(或NSArray
,NSDictionary
)等不可变属性经常使用copy
关键字?使用strong
关键字,会有什么问题?
示例5:
@interface Person : NSObject
@property(nonatomic ,strong) NSString * name;
@property(nonatomic ,copy) NSString * age;
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
Person * p = [[Person alloc]init];
NSMutableString * name = [NSMutableString stringWithFormat:@"马云爸爸"];
p.name = name;
NSLog(@"name修改前:%@", p.name);
[name appendString:@"和马化腾岳父谁厉害?"];
NSLog(@"name修改后:%@", p.name);
NSMutableString * age = [NSMutableString stringWithFormat:@"马云爸爸50岁"];
p.age = age;
NSLog(@"age修改前:%@", p.age);
[age appendString:@"马化腾岳父45岁"];
NSLog(@"age修改后:%@", p.age);
}
return 0;
}
打印结果
结果分析:Person
的name
属性用了strong
修饰,当传进来的NSMutableString
类型的name
变量值改变后Person
的name
属性值也发生了改变(父类指针指向子类,多态),而我们并没有对Person
的name
属性直接操作,这样是不合理的;而Person
的age
属性用了copy
修饰,不管传进来的变量是不是可变的字符串,都只会拷贝一份指针而指向该块内存,当可变的字符串age
值发生改变时(内存发生了改变),属性age
还是指向该块内存,不会改变。
- 如果用
copy
修饰可变对象会产生什么后果?
假如我们用copy关键字 来声明一个NSMutableString
对象。
示例6:
@interface Person : NSObject
@property(nonatomic ,copy) NSMutableString * name;
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
Person * p = [[Person alloc]init];
NSMutableString * name = [NSMutableString stringWithFormat:@"马云爸爸"];
p.name = name;
[p.name appendString:@"和马化腾岳父谁厉害?"];
}
return 0;
}
crash原因是:尝试使用appendString改变不可变对象
。
5.自定义对象使用copy和mutableCopy。
如果想自定义对象使用copy
和mutableCopy
,只需要分别遵守NSCopying
,NSMutableCopying
协议,然后实现- (id)copyWithZone:(nullable NSZone *)zone;
和- (id)mutableCopyWithZone:(nullable NSZone *)zone;
方法。
示例7:
#import <Foundation/Foundation.h>
@interface Person : NSObject<NSCopying,NSMutableCopying>
@property(nonatomic ,copy) NSString * name;
@property(nonatomic ,copy) NSString * age;
@end
#import "Person.h"
@implementation Person
-(id)copyWithZone:(NSZone *)zone{
Person * p = self;
return p;
}
-(id)mutableCopyWithZone:(NSZone *)zone{
Person * p = [[Person allocWithZone:zone] init];
p.name = self.name;
p.age = self.age;
return p;
}
int main(int argc, const char * argv[]) {
@autoreleasepool {
Person * p1 = [[Person alloc]init];
p1.name = @"马云";
p1.age = @"55";
Person * p2 = [p1 copy];
Person * p3 = [p1 mutableCopy];
}
return 0;
}