在OC语法中,提供了Copy语法(Copy + MutableCopy)用于对象的拷贝。其中很容易混淆的是浅拷贝和深拷贝。
所谓浅拷贝,即是地址拷贝,并不产生新的对象,而是对原对象的引用计数值加1。而深拷贝,即是对象拷贝,产生新的对象副本,计数器为1。
下面通过一个例子来分析一下这个比较容易乱的Copy:
一、对于NSString/NSMutableString; NSArray/NSMutableArray... 这OC提供的类对象:
以NSString/NSMutableString为例:
对于copy,返回的一定是不可变类型;而mutableCopy,返回的一定是可变类型。
①对于 mutableCopy ,一定是深拷贝。
如果是 NSString ---> NSString;则是浅拷贝;如果是 NSMutableString ---> NSString;则是 深拷贝。
如果是 NSString 、NSMutableString ---> NSMutableString;则是深拷贝。
注:只有一种情况下是发生浅拷贝:不可变对象 复制到 不可变对象。
1
2
3
4
5
6
7
8
9
10
11
|
//浅拷贝:指针拷贝 不会产生新的对象,源对象计数器加1
void
strCopy(){
NSString *str=[[NSString alloc]initWithFormat:@
"abcd"
];
//因为NSString对象本身就不可变,所以并没产生新的对象,而是返回对象本身,会做一次retain操作,所以源对象也会retain
NSString *str2=[str copy];
//输出二者的地址,二者的地址是相同的
NSLog(@
"str --> %p"
,str);
NSLog(@
"str2 --> %p"
,str2);
}
|
除了以上这种情形外,其他都是深拷贝。
例如:
1
2
3
4
5
6
7
8
9
10
11
12
|
//深拷贝
void
mutableStrCopy(){
NSMutableString *str=[[NSMutableString alloc]initWithFormat:@
"abcd"
];
//会产生一个新的对象计数器1
NSString *str2=[str copy];
//输出二者的地址,二者的地址是不同的
NSLog(@
"str --> %p"
,str);
NSLog(@
"str2 --> %p"
,str2);
}
|
二、对于自定义对象的Copy: 该类必须实现NSCopying协议,重写 copyWithZone 方法。
同理,对于自定义对象的mutableCopy:必须实现 NSMutableCopying 协议,重写 mutableCopyWithZone 方法。
在NSCopying协议中,其实只有一个协议方法:
1
2
3
|
@protocol
NSCopying
- (id)copyWithZone:(NSZone *)zone;
@end
|
在NSMutableCopying协议:
1
2
3
|
@protocol
NSMutableCopying
- (id)mutableCopyWithZone:(NSZone *)zone;
@end
|
下面给出一个例子:
1
2
3
4
5
6
7
8
9
10
|
#
import
<foundation foundation.h=
""
>
@interface
Person : NSObject<nscopying>
@property
(nonatomic,assign)
int
age;
@property
(nonatomic,copy)NSString *name;
-(instancetype)initWithAge:(
int
)age withName:(NSString*)name;
@end
</nscopying></foundation>
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
#
import
"Person.h"
@implementation
Person
-(instancetype)initWithAge:(
int
)age withName:(NSString*)name{
self = [
super
init];
if
(self) {
self.age = age;
self.name = name;
}
return
self;
}
-(id)copyWithZone:(NSZone *)zone{
Person* person = [[[self
class
] allocWithZone:zone] initWithAge:self.age withName:self.name];
return
person;
}
@end
|
1
2
3
4
5
6
7
8
9
10
11
12
13
|
int
main(
int
argc,
const
char
* argv[])
{
@autoreleasepool
{
Person *p1 = [[Person alloc] initWithAge:
20
withName:@
"Jack"
];
Person *p2 = [p1 copy];
//输出两个 Person 对象的地址,二者是不同的
NSLog(@
"p1 --> %p"
,p1);
NSLog(@
"p2 --> %p"
,p2);
}
return
0
;
}
|
加入对于某些自定义对象是不可变的,那么如何办呢?
1
2
3
|
-(id)copyWithZone:(NSZone *)zone{
return
self;
}
|
下面了解一下关于如果某一个自定义类继承了 这个Person类的情况。
如果某一个子类继承了实现了NSCopying协议的基类,那么该子类也是会自动继承这个协议的方法。但是需要自己重新实现。
例如:有一个Student子类继承了这个Person类:
1
2
3
4
5
6
7
8
9
|
#
import
<foundation foundation.h=
""
>
#
import
"Person.h"
@interface
Student : Person
@property
(nonatomic,copy)NSString *school;
-(instancetype)initWithAge:(
int
)age withName:(NSString *)name WithSchool:(NSString*)school;
@end
</foundation>
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
#
import
"Student.h"
@implementation
Student
-(instancetype)initWithAge:(
int
)age withName:(NSString *)name WithSchool:(NSString*)school{
self = [
super
initWithAge:age withName:name];
if
(self) {
self.school = school;
}
return
self;
}
-(id)copyWithZone:(NSZone *)zone{
Student *student = [
super
copyWithZone:zone];
student.school = self.school;
return
student;
}
@end
|
注意其中copyWithZone方法的实现。