1. 简介
(1) OC也提供了copy(copy + mutableCopy)语法,来实现对象的拷贝;对于拷贝我们要弄明白两个问题: 深层拷贝 和 浅层拷贝。
(2) 浅层拷贝 : 就是地址拷贝,并不铲射别难过新的对象,对象的引用计数器加1。
(3) 深层拷贝 : 就是对象拷贝,产生新的对象性副本,对象本身计数器计数器不变,副本的引用计数器为1.
(4) copy语法设计目的 : 改变副本的同时,不会影响到原对象。
(5) OC中可以使用获得该引用计数器的值来检查是否深拷贝。
2. 常用类的copy语法使用
a. copy
NSString,NSArray,NSSet,NSDictionary等等,copy比较特殊 : 它返回的是对象本身(浅层拷贝)。根据copy语法的设计目的,copy之后返回的是对象本身,因为它本身就不能被改变了。因此,常用OC类的设计者为了性能考虑,使NSString的copy返回对象本身的引用。
// ARC关闭
#import <Foundation/Foundation.h>
int main(int argc, const char * argv[]) {
@autoreleasepool {
/***************************************************** NSString **************************************************************/
// 创建一个 NSString 对象
NSString *str = [NSString stringWithFormat:@"age is %d",10];
// 检查引用计数器
NSLog(@"current count = %lu",[str retainCount]); // current count = 1
// str进行一次copy
NSString *strCopy = [str copy];
// 再次检查引用计数器,验证是否深拷贝
NSLog(@"current count = %lu",[str retainCount]); // current count = 2
// 进一步验证 NSString 的 copy 是否返回对象本身
NSLog(@"str == strCopy --> %@",strCopy == str ? @"true" : @"false"); // str == strCopy --> true
[strCopy release];
strCopy = nil;
/***************************************************** NSArray **************************************************************/
// 创建 OC 数组
NSArray *arr = @[@"one",@"two"];
// 检查引用计数器
NSLog(@"arr current count is %lu",[arr retainCount]); // arr current count is 1
// copy
NSArray *arrCopy = [arr copy];
// 验证 copy 是否深拷贝
NSLog(@"arr current count is %lu",[arr retainCount]); // arr current count is 2
NSLog(@"arrCopy current count is %lu",[arrCopy retainCount]); // arrCopy current count is 2
NSLog(@"arr == arrCopy --> %@",arr == arrCopy ? @"true" : @"false"); // arr == arrCopy --> true
[arrCopy release];
arrCopy = nil;
/***************************************************** NSSet ***************************************************************/
NSSet *set = [NSSet setWithObjects:@"one",@"two", nil];
NSLog(@"set current count is %lu",[set retainCount]); // set current count is 1
// copy
NSSet *setCopy = [set copy];
// 验证 copy 是否深拷贝
NSLog(@"set current count is %lu",[set retainCount]); // set current count is 2
NSLog(@"setCopy current count is %lu",[setCopy retainCount]); // setCopy current count is 2
NSLog(@"set == setCopy --> %@",set == setCopy ? @"true" : @"false"); // set == setCopy --> true
[setCopy release];
setCopy = nil;
/***************************************************** NSDictionary ***************************************************************/
NSDictionary *dict = @{@"1" : @"one" ,@"2" : @"two"};
NSLog(@"dict current count is %lu",[dict retainCount]); // dict current count is 1
// copy
NSDictionary *dictCopy = [dict copy];
// 验证 copy 是否深拷贝
NSLog(@"dict current count is %lu",[dict retainCount]); // dict current count is 2
NSLog(@"dictCopy current count is %lu",[dictCopy retainCount]); // dictCopy current count is 2
NSLog(@"dict == dictCopy --> %@",dict == dictCopy ? @"true" : @"false"); // set == setCopy --> true
[dictCopy release];
dictCopy = nil;
}
return 0;
}
// ARC关闭
#import <Foundation/Foundation.h>
int main(int argc, const char * argv[]) {
@autoreleasepool {
/***************************************************** NSString **************************************************************/
// 创建一个 NSString 对象
NSString *str = [NSString stringWithFormat:@"age is %d",10];
// 检查引用计数器
NSLog(@"current count = %lu",[str retainCount]); // current count = 1
// str进行一次copy
NSString *strCopy = [str copy];
// 再次检查引用计数器,验证是否深拷贝
NSLog(@"current count = %lu",[str retainCount]); // current count = 2
// 进一步验证 NSString 的 copy 是否返回对象本身
NSLog(@"str == strCopy --> %@",strCopy == str ? @"true" : @"false"); // str == strCopy --> true
[strCopy release];
strCopy = nil;
/***************************************************** NSArray **************************************************************/
// 创建 OC 数组
NSArray *arr = @[@"one",@"two"];
// 检查引用计数器
NSLog(@"arr current count is %lu",[arr retainCount]); // arr current count is 1
// copy
NSArray *arrCopy = [arr copy];
// 验证 copy 是否深拷贝
NSLog(@"arr current count is %lu",[arr retainCount]); // arr current count is 2
NSLog(@"arrCopy current count is %lu",[arrCopy retainCount]); // arrCopy current count is 2
NSLog(@"arr == arrCopy --> %@",arr == arrCopy ? @"true" : @"false"); // arr == arrCopy --> true
[arrCopy release];
arrCopy = nil;
/***************************************************** NSSet ***************************************************************/
NSSet *set = [NSSet setWithObjects:@"one",@"two", nil];
NSLog(@"set current count is %lu",[set retainCount]); // set current count is 1
// copy
NSSet *setCopy = [set copy];
// 验证 copy 是否深拷贝
NSLog(@"set current count is %lu",[set retainCount]); // set current count is 2
NSLog(@"setCopy current count is %lu",[setCopy retainCount]); // setCopy current count is 2
NSLog(@"set == setCopy --> %@",set == setCopy ? @"true" : @"false"); // set == setCopy --> true
[setCopy release];
setCopy = nil;
/***************************************************** NSDictionary ***************************************************************/
NSDictionary *dict = @{@"1" : @"one" ,@"2" : @"two"};
NSLog(@"dict current count is %lu",[dict retainCount]); // dict current count is 1
// copy
NSDictionary *dictCopy = [dict copy];
// 验证 copy 是否深拷贝
NSLog(@"dict current count is %lu",[dict retainCount]); // dict current count is 2
NSLog(@"dictCopy current count is %lu",[dictCopy retainCount]); // dictCopy current count is 2
NSLog(@"dict == dictCopy --> %@",dict == dictCopy ? @"true" : @"false"); // set == setCopy --> true
[dictCopy release];
dictCopy = nil;
}
return 0;
}
b. mutableCopy
mutableCopy,根据copy语法设计的目的返回一个可变对象(NSMutable),而且改变副本同时不影响原对象,因此只有深拷贝满足条件。
// ARC关闭
#import <Foundation/Foundation.h>
int main(int argc, const char * argv[]) {
@autoreleasepool {
/***************************************************** NSString **************************************************************/
// 创建一个 NSString 对象
NSString *str = [NSString stringWithFormat:@"age is %d",10];
// 检查引用计数器
NSLog(@"str current count = %lu",[str retainCount]); // str current count = 1
// NSString的 mutableCopy 返回的可变对象 NSMutableString
NSMutableString *strMutableCopy = [str mutableCopy];
// 修改副本
[strMutableCopy appendString:@" ,and name is mike"];
// 检查引用计数器,验证是 否深拷贝
NSLog(@"str : %@ current count = %lu",str,[str retainCount]); // age is 10 current count = 1
NSLog(@"str == strCopy --> %@",strMutableCopy == str ? @"true" : @"false"); // str == strCopy --> false
// mutableCopy 后的对象都是一个新的对象引用计数器为1
NSLog(@"strMutableCopy : %@ current count = %lu",strMutableCopy,[strMutableCopy retainCount]); // age is 10 ,and name is mike current count = 1
[strMutableCopy release];
/***************************************************** NSArray **************************************************************/
// 创建 OC 数组
NSArray *arr = @[@"one",@"two"];
// 检查引用计数器
NSLog(@"arr current count is %lu",[arr retainCount]); // arr current count is 1
// mutableCopy
NSMutableArray *arrMutable = [arr mutableCopy];
// 验证 mutableCopy 是否深拷贝
[arrMutable addObject:@"three"];
NSLog(@"arrMutable : %@",arrMutable); // arrMutable : (one,two,three)
NSLog(@"arr : %@",arr); // arr : (one,two)
NSLog(@"arr == arrMutable --> %@",arr == arrMutable ? @"true" : @"false"); // arr == arrMutable --> false
[arrMutable release];
arrMutable = nil;
/***************************************************** NSSet **************************************************************/
NSSet *set = [NSSet setWithObjects:@"one",@"two", nil];
NSLog(@"set current count is %lu",[set retainCount]); // set current
// mutableCopy
NSMutableSet *setMutable = [set mutableCopy];
// 验证 mutableCopy 是否深拷贝
[setMutable addObject:@"three"];
NSLog(@"arrMutable : %@",setMutable); // setMutable : {(one,two,three)}
NSLog(@"set : %@",set); // set : {(one,two)}
NSLog(@"set == setMutable --> %@",set == setMutable ? @"true" : @"false"); // set == setMutable --> false
[setMutable release];
setMutable = nil;
/***************************************************** NSMutableDictionary **************************************************************/
NSDictionary *dict = @{@"1" : @"one" ,@"2" : @"two"};
NSLog(@"dict current count is %lu",[dict retainCount]); // dict current is 1
NSMutableDictionary *dictMutable = [dict mutableCopy];
// 验证 mutableCopy 是否深拷贝
[dictMutable setObject:@"3" forKey:@"three"];
NSLog(@"dictMutable : %@",dictMutable); // dictMutable : {1 = one ;2 = two ;3 = three}
NSLog(@"dict : %@",dict); // dict : {1 = one ;2 = two}
NSLog(@"dict == dictMutable --> %@",dict == dictMutable ? @"true" : @"false"); // dict == dictMutable --> false
[dictMutable release];
dictMutable = nil;
}
return 0;
}
// ARC关闭
#import <Foundation/Foundation.h>
int main(int argc, const char * argv[]) {
@autoreleasepool {
/***************************************************** NSString **************************************************************/
// 创建一个 NSString 对象
NSString *str = [NSString stringWithFormat:@"age is %d",10];
// 检查引用计数器
NSLog(@"str current count = %lu",[str retainCount]); // str current count = 1
// NSString的 mutableCopy 返回的可变对象 NSMutableString
NSMutableString *strMutableCopy = [str mutableCopy];
// 修改副本
[strMutableCopy appendString:@" ,and name is mike"];
// 检查引用计数器,验证是 否深拷贝
NSLog(@"str : %@ current count = %lu",str,[str retainCount]); // age is 10 current count = 1
NSLog(@"str == strCopy --> %@",strMutableCopy == str ? @"true" : @"false"); // str == strCopy --> false
// mutableCopy 后的对象都是一个新的对象引用计数器为1
NSLog(@"strMutableCopy : %@ current count = %lu",strMutableCopy,[strMutableCopy retainCount]); // age is 10 ,and name is mike current count = 1
[strMutableCopy release];
/***************************************************** NSArray **************************************************************/
// 创建 OC 数组
NSArray *arr = @[@"one",@"two"];
// 检查引用计数器
NSLog(@"arr current count is %lu",[arr retainCount]); // arr current count is 1
// mutableCopy
NSMutableArray *arrMutable = [arr mutableCopy];
// 验证 mutableCopy 是否深拷贝
[arrMutable addObject:@"three"];
NSLog(@"arrMutable : %@",arrMutable); // arrMutable : (one,two,three)
NSLog(@"arr : %@",arr); // arr : (one,two)
NSLog(@"arr == arrMutable --> %@",arr == arrMutable ? @"true" : @"false"); // arr == arrMutable --> false
[arrMutable release];
arrMutable = nil;
/***************************************************** NSSet **************************************************************/
NSSet *set = [NSSet setWithObjects:@"one",@"two", nil];
NSLog(@"set current count is %lu",[set retainCount]); // set current
// mutableCopy
NSMutableSet *setMutable = [set mutableCopy];
// 验证 mutableCopy 是否深拷贝
[setMutable addObject:@"three"];
NSLog(@"arrMutable : %@",setMutable); // setMutable : {(one,two,three)}
NSLog(@"set : %@",set); // set : {(one,two)}
NSLog(@"set == setMutable --> %@",set == setMutable ? @"true" : @"false"); // set == setMutable --> false
[setMutable release];
setMutable = nil;
/***************************************************** NSMutableDictionary **************************************************************/
NSDictionary *dict = @{@"1" : @"one" ,@"2" : @"two"};
NSLog(@"dict current count is %lu",[dict retainCount]); // dict current is 1
NSMutableDictionary *dictMutable = [dict mutableCopy];
// 验证 mutableCopy 是否深拷贝
[dictMutable setObject:@"3" forKey:@"three"];
NSLog(@"dictMutable : %@",dictMutable); // dictMutable : {1 = one ;2 = two ;3 = three}
NSLog(@"dict : %@",dict); // dict : {1 = one ;2 = two}
NSLog(@"dict == dictMutable --> %@",dict == dictMutable ? @"true" : @"false"); // dict == dictMutable --> false
[dictMutable release];
dictMutable = nil;
}
return 0;
}
3. copy语法在自定义对象中的用法
(1) 我们打开 NSString 的声明发现它遵守了NSCopying协议,因此我们如果想在自定义对象中实现使用copy语法,必须遵守 NSCopying 协议,并实现
- (id)copyWithZone:(NSZone *)zone 方法。
- (id)copyWithZone:(NSZone *)zone 方法。
// ARC关闭
/************************************** Person.h **************************************/
#import <Foundation/Foundation.h>
// 如果要使用copy语法必须遵守 NSCopying 协议
@interface Person : NSObject <NSCopying>
/*
* copy是释放旧值,copy新值
* 对于set方法 :
* 参数 : 可变对象 -> 深层拷贝
* 不可变参数 -> 浅层拷贝
*/
@property (nonatomic,copy) NSString *name;
@end
/************************************** Person.m **************************************/
#import "Person.h"
@implementation Person
+ (id) initWithName :(NSString *) name
{
Person *p = [[self alloc] init];
p.name = name;
return p;
}
- (id)copyWithZone:(NSZone *)zone
{
NSLog(@"调用copy...");
// 对于使用 copy 语法,内存释放由调用者完成
Person *copy = [[[self class] allocWithZone:zone] init];
copy.name = _name;
return copy;
}
-(NSString *)description
{
NSString *des = [NSString stringWithFormat:@"name : %@ , name的地址 : %p",_name,_name];
return des;
}
-(void)dealloc
{
[_name release];
[super dealloc];
}
@end
/************************************** main.m **************************************/
/*
* copy参数的验证示例
*/
#import <Foundation/Foundation.h>
#import "Person.h"
int main(int argc, const char * argv[]) {
@autoreleasepool {
// 不可变字符串
// NSString *str1 = [NSString stringWithFormat:@"age is %d",10];
// Person *p = [[[Person alloc] init] autorelease];
// p.name = str1;
// 判断对象的地址是否一样来检验是否完成深拷贝
// NSLog(@"%d",str1 == p.name); // 打印结果为 1 则说明完成浅拷贝
// 可变字符串
NSMutableString *str1 = [NSMutableString stringWithFormat:@"age is %d",10];
Person *p = [[[Person alloc] init] autorelease];
p.name = str1;
// 判断对象的地址是否一样来检验是否完成深拷贝
NSLog(@"%d",str1 == p.name); // 打印结果为 0 则说明完成深拷贝
}
return 0;
}
/*
* 对象copy语法实现深层拷贝验证
*/
#import <Foundation/Foundation.h>
#import "Person.h"
int main(int argc, const char * argv[]) {
@autoreleasepool {
// 不可变对象
NSString *name = [NSString stringWithFormat:@"%@",@"jack"];
Person *p = [[[Person alloc] init] autorelease];
p.name = name;
Person *p2 =[[p copy] autorelease]; // 调用copy...
NSLog(@"p 地址: %p ,p : %@",&p,p); // p 地址: 0x7fff5fbff870 ,p : name : jack , name的地址 : 0x1002040f0
NSLog(@"p2 地址: %p ,p2 : %@",&p2,p2); // p2 地址: 0x7fff5fbff868 ,p2 : name : jack , name的地址 : 0x1002040f0
}
return 0;
}
// ARC关闭
/************************************** Person.h **************************************/
#import <Foundation/Foundation.h>
// 如果要使用copy语法必须遵守 NSCopying 协议
@interface Person : NSObject <NSCopying>
/*
* copy是释放旧值,copy新值
* 对于set方法 :
* 参数 : 可变对象 -> 深层拷贝
* 不可变参数 -> 浅层拷贝
*/
@property (nonatomic,copy) NSString *name;
@end
/************************************** Person.m **************************************/
#import "Person.h"
@implementation Person
+ (id) initWithName :(NSString *) name
{
Person *p = [[self alloc] init];
p.name = name;
return p;
}
- (id)copyWithZone:(NSZone *)zone
{
NSLog(@"调用copy...");
// 对于使用 copy 语法,内存释放由调用者完成
Person *copy = [[[self class] allocWithZone:zone] init];
copy.name = _name;
return copy;
}
-(NSString *)description
{
NSString *des = [NSString stringWithFormat:@"name : %@ , name的地址 : %p",_name,_name];
return des;
}
-(void)dealloc
{
[_name release];
[super dealloc];
}
@end
/************************************** main.m **************************************/
/*
* copy参数的验证示例
*/
#import <Foundation/Foundation.h>
#import "Person.h"
int main(int argc, const char * argv[]) {
@autoreleasepool {
// 不可变字符串
// NSString *str1 = [NSString stringWithFormat:@"age is %d",10];
// Person *p = [[[Person alloc] init] autorelease];
// p.name = str1;
// 判断对象的地址是否一样来检验是否完成深拷贝
// NSLog(@"%d",str1 == p.name); // 打印结果为 1 则说明完成浅拷贝
// 可变字符串
NSMutableString *str1 = [NSMutableString stringWithFormat:@"age is %d",10];
Person *p = [[[Person alloc] init] autorelease];
p.name = str1;
// 判断对象的地址是否一样来检验是否完成深拷贝
NSLog(@"%d",str1 == p.name); // 打印结果为 0 则说明完成深拷贝
}
return 0;
}
/*
* 对象copy语法实现深层拷贝验证
*/
#import <Foundation/Foundation.h>
#import "Person.h"
int main(int argc, const char * argv[]) {
@autoreleasepool {
// 不可变对象
NSString *name = [NSString stringWithFormat:@"%@",@"jack"];
Person *p = [[[Person alloc] init] autorelease];
p.name = name;
Person *p2 =[[p copy] autorelease]; // 调用copy...
NSLog(@"p 地址: %p ,p : %@",&p,p); // p 地址: 0x7fff5fbff870 ,p : name : jack , name的地址 : 0x1002040f0
NSLog(@"p2 地址: %p ,p2 : %@",&p2,p2); // p2 地址: 0x7fff5fbff868 ,p2 : name : jack , name的地址 : 0x1002040f0
}
return 0;
}
4. 总结
改变副本的同时,不会影响到原对象
- (id)copyWithZone:(NSZone *)zone 方法。
copy --> 浅层拷贝 mutableCopy --> 深层拷贝