copy 关键字的作用
从一个例子说起:
@interface Person : NSObject @property (nonatomic, copy) NSString *name; @end Person *xiaohong = [[Person alloc] init];
这段代码中
NSString
为什么要加上copy关键字呢?
copy
关键字的作用是,调用obj
(已经遵从NSCoding
)的copyWithZone:
,并将返回值赋值给实例变量。


上图说明了深浅拷贝的内存分配情况,不分配内存的情况下,将增加引用计数(
ARC
)。
容易疑惑的地方:
- B2 为什么是浅拷贝
- C3 为什么不安全
- E2 为什么不安全
- D3 为什么不安全
不可变类型(以NSString
为例)
NSString
类比C++代码如下(未区分堆栈):
NSString *strOC = @"hello oc"; // 指针是可变的,但是所指的内存区域是不可变的
const char strC[] = "hello cpp"; // c++下类似的情况
strOC = @"new"; // strOC指向的区域发生变化

疑惑点1
形如
@property (copy) NSString *name; // Person中定义 xiaohong.name = [NSString stringWithFormat:@"xiaohong"];
在不可变类型的属性指定了copy
关键字后,并没有分配新的内存,这是因为已经指定属性为不可变的类型,所以此块内存区域的值是不会发生变化,这是为了为节约内存和提高效率(分配回收内存需要时间)。
疑惑点2
考虑如下代码
@property (copy) NSString *name; // Person中定义 NSMutableString *name = [NSMutableString stringWithFormat:@"xiaohong"]; xiaohong.name = name; [name appendString:@" Zhang"]; NSLog(@"%@", xiaohone.name); // "xiaohong zhang"
这里说的不安全是因为在类中已经将name
定义为NSString
类型,所以说希望该内存是不可变的,但是又在外部修改了它的值,与期望相悖,而且用NSString
的指向一个NSMutableNSString
这也是不安全的。
疑惑点3
考虑如下代码
@property NSMutableString *name; // Person中定义 xiaohong.name = [NSString stringWithFormat:@"xiaohong"]; [xiaohone.name appendString:@" zhang"]; // crash
造成crash的原因为向NSString
发送了appendString:
的消息,但是NSString
不能处理此消息。因为copy
关键字调用copyWithZone:
,生成的是不可变类型(即NSString
),即使指针被转换为了NSMutableString
类型,但是runtime
会根据class
中的信息发送消息。
copy 的作用如下
@property NSMutableString *name; // Person中定义 // setter中:self.name = [[NSString stringWithFormat:@"xiaohong"] copy]; // self.name实际是NSString类型 xiaohong.name = [NSString stringWithFormat:@"xiaohong"]; [xiaohone.name appendString:@" zhang"]; // crash
疑惑点4
@property (copy) NSMutableString *name; // Person中定义 // setter中:self.name = [[NSMutableString stringWithFormat:@"xiaohong"] copy]; // copy 产生的不可变对象NSString // self.name实际是NSString类型 xiaohong.name = [NSMutableString stringWithFormat:@"xiaohong"]; [xiaohone.name appendString:@" zhang"]; // crash, 因为name的类型是NSString
对于OC中容器类型的拷贝
容器类型的行为与OC中其他引用类型的行为是一致的。所以说如果容器中存放的是引用类型,则即使发生深拷贝,也只是拷贝了容器中的引用。所以说容器的深拷贝只是对容器来说的。
例如
NSMutableString *mStr = [NSMutableString stringWithFormat:@"xiaohong"]; NSMutableArray *mAry = [NSMutableArray arrayWithObject:mStr]; NSMutableArray *mCAry = mAry.mutableCopy; [mstr appendString: @" zhang"]; // mAry[0]与mCAry[0]指向相同内存 NSLog(mAry[0]); // "xiaohong zhang" NSLog(mCAry[0]); // "xiaohong zhang"
copy
和mutableCopy
属性将分别调用copyWithZone:
和mutableCopyWithZone:
把自定义的类作为有copy关键字的属性
需要实现copyWithZone:
,如果自定义的类包含可变和不可变两种类型,则需要分别实现copyWithZone:
和mutableCopyWithZone:
,copy
的行为是自己定义的,当然正常情况下应该是定义为深拷贝。
网友评论