首先,什么是copy?
Copy的字面意思是“复制”、“拷贝”,是一个产生副本的过程。
常见的复制有:文件复制,作用是利用一个源文件产生一个副本文件。
特点:1、修改源文件的内容,不会影响副本文件;
2、修改副本文件的内容,不会影响源文件。
OC中copy的作用是:利用一个源对象产生一个副本对象
特点:1、修改源对象的属性和行为,不会影响副本对象;
2、修改副本对象的属性和行为,不会影响源对象。
如何使用copy功能?
一个对象可以调用copy或mutableCopy方法来创建一个副本对象。
1、copy:创建的时不可变副本(如NSString、NSArray、NSDictionary)。
2、mutableCopy:创建的可变副本(如NSMutableString、NSMutableArray、NSMutableDictionary)。
使用copy功能的前提:
1、copy:需要遵守NSCopying协议,实现copyWithZone:方法。
@protocol NSCopying
- (id)copyWithZone:(NSZone *)zone;
2、mutableCopy : 需要遵守NSMutableCopying协议,实现mutableCopyWithZone:方法
@protocol NSMutableCopying
- (id)mutableCopyWithZone:(NSZone *)zone;
@end
深复制和浅复制的区别:
深复制(深拷贝、内容拷贝、deep copy):
特点:1、源对象和副本对象是不同的两个对象;
2、源对象引用计数器不变,副本对象计数器为1(因为是新产生的)。
本质:产生了新对象。
浅复制(浅拷贝、指针拷贝、shallow copy):
特点:1、源对象和副本对象是同一对象;
2、源对象(副本对象)引用计数器+1,相当于做一次retain操作。
本质:没有产生新对象。
常见的复制如下图:
只有源对象和副本对象都不可变时,才是浅复制,其他都是深复制。
关于区分深复制与浅复制的一些详细代码如下:
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
|
/** NSMutableString调用mutableCopy : 深复制 */void mutableStringMutableCopy(){ NSMutableString *srcStr = [NSMutableString stringWithFormat:@"age is %d", 10]; NSMutableString *copyStr = [srcStr mutableCopy]; [copyStr appendString:@"abc"]; NSLog(@"srcStr=%@, copyStr=%@", srcStr, copyStr);}/** NSMutableString调用copy : 深复制 */void mutableStringCopy(){ NSMutableString *srcStr = [NSMutableString stringWithFormat:@"age is %d", 10]; NSString *copyStr = [srcStr copy]; [srcStr appendString:@"abc"]; NSLog(@"srcStr=%p, copyStr=%p", srcStr, copyStr);}/** NSString调用mutableCopy : 深复制 */void stringMutableCopy(){ NSString *srcStr = [NSString stringWithFormat:@"age is %d", 10]; NSMutableString *copyStr = [srcStr mutableCopy]; [copyStr appendString:@"abc"]; NSLog(@"srcStr=%@, copyStr=%@", srcStr, copyStr);}/** NSString调用copy : 浅复制 */void stringCopy(){ // copy : 产生的肯定是不可变副本 // 如果是不可变对象调用copy方法产出不可变副本,那么不会产生新的对象 NSString *srcStr = [NSString stringWithFormat:@"age is %d", 10]; NSString *copyStr = [srcStr copy]; NSLog(@"%p %p", srcStr, copyStr);} |
@property内存管理策略的选择
1.非ARC
1> copy : 只用于NSString\block;
2> retain : 除NSString\block以外的OC对象;
3> assign : 基本数据类型、枚举、结构体(非OC对象),当2个对象相互引用,一端用retain,一端 用assign。
2.ARC
1> copy : 只用于NSString\block;
2> strong : 除NSString\block以外的OC对象;
3> weak : 当2个对象相互引用,一端用strong,一端用weak;
4> assgin : 基本数据类型、枚举、结构体(非OC对象)。
============================================================
所谓copy就是在原有对象的基础上产生一个副本对象,遵循最关键的两点原则:
1. 改变原对象的属性和行为不会对副本对象产生任何影响
2. 改变副本对象的属性和行为不会对原对象产生任何影响
在理解了这一层之后,我们一起来研究下deep copy 和 shallow copy,因为苹果是一个非常注重性能的公司,所以拷贝在底层实现没那么简单:
以NSString为案例,以下四种情况彻底说明了什么是深复制和浅复制:
(一) 如果是不可变对象调用copy方法产出不可变副本,那么不会产生新的对象, 因此这是shallow copy,不会产生新的对象,指针地址相等.
int main(int argc, const char * argv[]){@autoreleasepool {NSString *srcStr = @"copy";NSString *copyStr = [srcStr copy];NSLog(@"%p %p", srcStr, copyStr);}return 0;}2014-05-25 22:26:13.805 03-复制[47093:303] 0x100001080 0x100001080
(二) 如果是不可变对象调用mutableCopy方法产出可变副本,那么一定会产生新的对象, 因此这是deep copy,会产生新的对象,指针地址不相等.
int main(int argc, const char * argv[]){ @autoreleasepool { NSString *srcStr = @"copy"; NSMutableString *copyStr =[srcStr mutableCopy]; NSLog(@"srcStr=%p, copyStr=%p", srcStr, copyStr); } return 0;}2014-05-25 22:32:22.598 03-复制[47519:303] srcStr=0x100001080, copyStr=0x100203f70
(三) 如果是可变对象调用copy方法产出不可变副本,那么一定会产生新的对象, 因此这是deep copy,会产生新的对象,指针地址不相等.
int main(int argc, const char * argv[]){ @autoreleasepool { NSMutableString *srcStr = [NSMutableString stringWithFormat:@"copy"]; NSString *copyStr = [srcStr copy]; NSLog(@"srcStr=%p, copyStr=%p", srcStr, copyStr); } return 0;}2014-05-25 22:35:37.218 03-复制[47754:303] srcStr=0x10010afd0, copyStr=0x1001031e0
(四) 如果是可变对象调用copy方法产出可变副本,那么一定会产生新的对象, 因此这是deep copy,会产生新的对象,指针地址不相等.
int main(int argc, const char * argv[]){ @autoreleasepool { NSMutableString *srcStr = [NSMutableString stringWithFormat:@"copy"]; NSMutableString *copyStr = [srcStr mutableCopy]; NSLog(@"srcStr=%p, copyStr=%p", srcStr, copyStr); } return 0;}2014-05-25 22:37:40.300 03-复制[47897:303] srcStr=0x100203f70, copyStr=0x100204080
只有在第一种情况中才是浅复制,可以理解下复制的本意:虽然两对象内容相同,但绝对互不影响.为什么第一种是浅复制呢?因为是一个不可变的拷贝生产另外一个不可变的,既然都不可变,那干脆指向同一个内存空间.因为"苹果"很注重性能.
由于现在都在ARC下开发,非ARC下开发很少,但是还是来分析下其运作流程,以便巩固下知识:
两种情况,分别来分析:
(一) 在进行浅复制时,虽然没有产出新对象, 但是源对象和副本对象的计数器都会+1:
int main(int argc, const char * argv[]){@autoreleasepool {NSString *str = [NSString stringWithFormat:@"Copy"];NSString *str2 = [str copy]; // [str2 release];NSLog(@"%zd, %zd", [str retainCount], [str2 retainCount]);}return 0;}2014-05-25 22:48:28.151 04-复制和计数器[48639:303] 2, 2
为什么呢?这里牵扯到OC的内存管理原则了,凡事new,retain,copy..时,计数器都会+1,因为产生了一个新的对象,但是,如果是:
NSString *str = @"1"; // 一个常量 结果就是
2014-05-25 22:48:28.151 04-复制和计数器[48639:303] -1, -1
所有,在iOS中NSString是严格遵守NSCopying和NSMutableCopying的.
(二) 在进行深复制时,产出新对象, 但是源对象的计数器不变,副本对象的计数器+1:
int main(int argc, const char * argv[]){ @autoreleasepool { NSString *str = @"Copy"; NSMutableString *str2 = [str mutableCopy]; NSLog(@"str=%zd, str2=%zd", [str retainCount], [str2 retainCount]); // [str2 release]; } return 0;}2014-05-25 22:59:08.688 04-复制[49397:303] str=-1, str2=1
运用copy的终极目的是: 当我赋值后,改变旧值不会影响新值. 如果要改变,就用retain..... 还有一点copy的底层实现,不就彻底弄清了:
- (void) setTitle : (NSString *)title{ if (_title != title){ [_title release]; _title = [title copy]; }}- (void)dealloc{ self.title = nil; [super dealloc];}

浙公网安备 33010602011771号