Objective-C中的copy

copy 关键字的作用

从一个例子说起:

@interface Person : NSObject
@property (nonatomic, copy) NSString *name;
@end
Person *xiaohong = [[Person alloc] init];

这段代码中NSString为什么要加上copy关键字呢?

copy关键字的作用是,调用obj(已经遵从NSCoding)的copyWithZone:,并将返回值赋值给实例变量

copy关键字的作用

深浅拷贝

上图说明了深浅拷贝的内存分配情况,不分配内存的情况下,将增加引用计数(ARC)。

容易疑惑的地方:

  1. B2 为什么是浅拷贝
  2. C3 为什么不安全
  3. E2 为什么不安全
  4. D3 为什么不安全

不可变类型(以NSString为例)

NSString类比C++代码如下(未区分堆栈):

NSString *strOC = @"hello oc";     // 指针是可变的,但是所指的内存区域是不可变的
const char strC[] = "hello cpp";   // c++下类似的情况
strOC = @"new"; // strOC指向的区域发生变化
内存2

疑惑点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"

copymutableCopy属性将分别调用copyWithZone:mutableCopyWithZone:

把自定义的类作为有copy关键字的属性

需要实现copyWithZone:,如果自定义的类包含可变和不可变两种类型,则需要分别实现copyWithZone:mutableCopyWithZone:copy的行为是自己定义的,当然正常情况下应该是定义为深拷贝。

你可能感兴趣的:(Objective-C中的copy)