copy关键字

用途:
1.NSString、NSArray、NSDictionary 等等经常使用copy关键字,是因为他们有对应的可变类型:NSMutableString、NSMutableArray、NSMutableDictionary;
block 也经常使用 copy 关键字,具体原因见官方文档:Objects Use Properties to Keep Track of Blocks
2.block 使用 copy 是从 MRC 遗留下来的“传统”,在 MRC 中,方法内部的 block 是在栈区的,使用 copy 可以把它放到堆区.在 ARC 中写不写都行:对于 block 使用 copy 还是 strong 效果是一样的,但写上 copy 也无伤大雅,还能时刻提醒我们:编译器自动对 block 进行了 copy 操作。如果不写 copy ,该类的调用者有可能会忘记或者根本不知道“编译器会自动对 block 进行了 copy 操作”,他们有可能会在调用之前自行拷贝属性值。这种操作多余而低效。

mark1.png

下面做下解释: copy 此特质所表达的所属关系与 strong 类似。然而设置方法并不保留新值,而是将其“拷贝” (copy)。 当属性类型为 NSString 时,经常用此特质来保护其封装性,因为传递给设置方法的新值有可能指向一个 NSMutableString 类的实例。这个类是 NSString 的子类,表示一种可修改其值的字符串,此时若是不拷贝字符串,那么设置完属性之后,字符串的值就可能会在对象不知情的情况下遭人更改。所以,这时就要拷贝一份“不可变” (immutable)的字符串,确保对象中的字符串值不会无意间变动。只要实现属性所用的对象是“可变的” (mutable),就应该在设置新属性值时拷贝一份。

用 @property 声明 NSString、NSArray、NSDictionary 经常使用 copy 关键字,是因为他们有对应的可变类型:NSMutableString、NSMutableArray、NSMutableDictionary,他们之间可能进行赋值操作,为确保对象中的字符串值不会无意间变动,应该在设置新属性值时拷贝一份。

该问题在下文中也有论述:用@property声明的NSString(或NSArray,NSDictionary)经常使用copy关键字,为什么?如果改用strong关键字,可能造成什么问题?

这个写法会出什么问题: @property (copy) NSMutableArray *array;

两个问题:
1、添加,删除,修改数组内的元素的时候,程序会因为找不到对应的方法而崩溃.因为 copy 就是复制一个不可变 NSArray 的对象;
2、使用了 atomic 属性会严重影响性能 ;

第1条的相关原因在下文中有论述《用@property声明的NSString(或NSArray,NSDictionary)经常使用 copy 关键字,为什么?如果改用strong关键字,可能造成什么问题?》 以及上文《怎么用 copy 关键字?》也有论述。
比如下面的代码就会发生崩溃

@property (nonatomic, copy) NSMutableArray *mutableArray;
// 下面的代码就会发生崩溃
NSMutableArray *array = [NSMutableArray arrayWithObjects:@1,@2,nil];
self.mutableArray = array;
[self.mutableArray removeObjectAtIndex:0];

接下来就会奔溃:
-[__NSArrayI removeObjectAtIndex:]: unrecognized selector sent to instance 0x7fcd1bc30460

第2条原因,如下:

该属性使用了同步锁,会在创建时生成一些额外的代码用于帮助编写多线程程序,这会带来性能问题,通过声明 nonatomic 可以节省这些虽然很小但是不必要额外开销。

在默认情况下,由编译器所合成的方法会通过锁定机制确保其原子性(atomicity)。如果属性具备 nonatomic 特质,则不使用同步锁。请注意,尽管没有名为“atomic”的特质(如果某属性不具备 nonatomic 特质,那它就是“原子的”(atomic))。
在iOS开发中,你会发现,几乎所有属性都声明为 nonatomic。
一般情况下并不要求属性必须是“原子的”,因为这并不能保证“线程安全” ( thread safety),若要实现“线程安全”的操作,还需采用更为深层的锁定机制才行。例如,一个线程在连续多次读取某属性值的过程中有别的线程在同时改写该值,那么即便将属性声明为 atomic,也还是会读到不同的属性值。
因此,开发iOS程序时一般都会使用 nonatomic 属性。但是在开发 Mac OS X 程序时,使用 atomic 属性通常都不会有性能瓶颈。

如何让自己的类用 copy 修饰符?如何重写带 copy 关键字的 setter?

若想令自己所写的对象具有拷贝功能,则需实现 NSCopying 协议。如果自定义的对象分为可变版本与不可变版本,那么就要同时实现 NSCopying 与 NSMutableCopying 协议。

具体步骤:
1.需声明该类遵从 NSCopying 协议
2.实现 NSCopying 协议。该协议只有一个方法:

- (id)copyWithZone:(NSZone *)zone;

注意:一提到让自己的类用 copy 修饰符,我们总是想覆写copy方法,其实真正需要实现的却是 “copyWithZone” 方法。
e.g.

typedef NS_ENUM(NSInteger, CYLSex) {
 CYLSexMan,
 CYLSexWoman
 };

@interface CYLUser : NSObject<NSCopying> 

@property (nonatomic, readonly, copy) NSString *name;
@property (nonatomic, readonly, assign) NSUInteger age;
@property (nonatomic, readonly, assign) CYLSex sex;
 - (instancetype)initWithName:(NSString *)name age:(NSUInteger)age sex:(CYLSex)sex;
 + (instancetype)userWithName:(NSString *)name age:(NSUInteger)age sex:(CYLSex)sex; 

@end

然后实现协议中规定的方法:

- (id)copyWithZone:(NSZone *)zone {
   CYLUser *copy = [[[self class] allocWithZone:zone] initWithName:_name age:_age sex:_sex];
    return copy;
}

用@property声明的NSString(或NSArray,NSDictionary)经常使用copy关键字,为什么?如果改用strong关键字,可能造成什么问题?

1.因为父类指针可以指向子类对象,使用 copy 的目的是为了让本对象的属性不受外界影响,使用 copy 无论给我传入是一个可变对象还是不可对象,我本身持有的就是一个不可变的副本.
2.如果我们使用是 strong ,那么这个属性就有可能指向一个可变对象,如果这个可变对象在外部被修改了,那么会影响该属性.
copy 此特质所表达的所属关系与 strong 类似。然而设置方法并不保留新值,而是将其“拷贝” (copy)。 当属性类型为 NSString 时,经常用此特质来保护其封装性,因为传递给设置方法的新值有可能指向一个 NSMutableString 类的实例。这个类是 NSString 的子类,表示一种可修改其值的字符串,此时若是不拷贝字符串,那么设置完属性之后,字符串的值就可能会在对象不知情的情况下遭人更改。所以,这时就要拷贝一份“不可变” (immutable)的字符串,确保对象中的字符串值不会无意间变动。只要实现属性所用的对象是“可变的” (mutable),就应该在设置新属性值时拷贝一份。
举例说明:
定义一个以 strong 修饰的 array:

@property (nonatomic ,readwrite, strong) NSArray *array;

然后进行下面的操作:

  NSMutableArray *mutableArray = [[NSMutableArray alloc] init];
  NSArray *array = @[ @1, @2, @3, @4 ];
  self.array = mutableArray;
  [mutableArray removeAllObjects];
  NSLog(@"%@",self.array);

  [mutableArray addObjectsFromArray:array];
  self.array = [mutableArray copy];
  [mutableArray removeAllObjects];
  NSLog(@"%@",self.array);

打印结果如下所示:

2015-09-27 19:10:32.523 CYLArrayCopyDmo[10681:713670] (
)
2015-09-27 19:10:32.524 CYLArrayCopyDmo[10681:713670] (
1,
2,
3,
4
)

为了理解这种做法,首先要知道,两种情况:
对非集合类对象的 copy 与 mutableCopy 操作;
对集合类对象的 copy 与 mutableCopy 操作。

  1. 对非集合类对象的copy操作:

在非集合类对象中:对 immutable 对象进行 copy 操作,是指针复制,mutableCopy 操作时内容复制;对 mutable 对象进行 copy 和 mutableCopy 都是内容复制。用代码简单表示如下:

[immutableObject copy] // 浅复制
[immutableObject mutableCopy] //深复制
[mutableObject copy] //深复制
[mutableObject mutableCopy] //深复制

比如以下代码:

NSMutableString *string = [NSMutableString stringWithString:@"origin"];//copy
NSString *stringCopy = [string copy];

查看内存,会发现 string、stringCopy 内存地址都不一样,说明此时都是做内容拷贝、深拷贝。即使你进行如下操作:

[string appendString:@"origion!"]

stringCopy 的值也不会因此改变,但是如果不使用 copy,stringCopy 的值就会被改变。 集合类对象以此类推。 所以,

用 @property 声明 NSString、NSArray、NSDictionary 经常使用 copy 关键字,是因为他们有对应的可变类型:NSMutableString、NSMutableArray、NSMutableDictionary,他们之间可能进行赋值操作,为确保对象中的字符串值不会无意间变动,应该在设置新属性值时拷贝一份。

2、集合类对象的copy与mutableCopy

集合类对象是指 NSArray、NSDictionary、NSSet ... 之类的对象。下面先看集合类immutable对象使用 copy 和 mutableCopy 的一个例子:

NSArray *array = @[@[@"a", @"b"], @[@"c", @"d"]];
NSArray *copyArray = [array copy];
NSMutableArray *mCopyArray = [array mutableCopy];

查看内容,可以看到 copyArray 和 array 的地址是一样的,而 mCopyArray 和 array 的地址是不同的。说明 copy 操作进行了指针拷贝,mutableCopy 进行了内容拷贝。但需要强调的是:此处的内容拷贝,仅仅是拷贝 array 这个对象,array 集合内部的元素仍然是指针拷贝。这和上面的非集合 immutable 对象的拷贝还是挺相似的,那么mutable对象的拷贝会不会类似呢?我们继续往下,看 mutable 对象拷贝的例子:

NSMutableArray *array = [NSMutableArray arrayWithObjects:[NSMutableString stringWithString:@"a"],@"b",@"c",nil];
NSArray *copyArray = [array copy];
NSMutableArray *mCopyArray = [array mutableCopy];

查看内存,如我们所料,copyArray、mCopyArray和 array 的内存地址都不一样,说明 copyArray、mCopyArray 都对 array 进行了内容拷贝。同样,我们可以得出结论:

在集合类对象中,对 immutable 对象进行 copy,是指针复制, mutableCopy 是内容复制;对 mutable 对象进行 copy 和 mutableCopy 都是内容复制。但是:集合对象的内容复制仅限于对象本身,对象元素仍然是指针复制。用代码简单表示如下:

[immutableObject copy] // 浅复制
[immutableObject mutableCopy] //单层深复制
[mutableObject copy] //单层深复制
[mutableObject mutableCopy] //单层深复制

这个代码结论和非集合类的非常相似。

文/TEASON(简书作者)
原文链接:http://www.jianshu.com/p/4e327d1832ef
著作权归作者所有,转载请联系作者获得授权,并标注“简书作者”。

时间: 2024-10-18 04:53:23

copy关键字的相关文章

怎么用 copy 关键字?

NSString.NSArray.NSDictionary等等经常使用copy关键字,是因为他们有对应的可变类型:NSMutableString.NSMutableArray.NSMutableDictionary,为确保对象中的属性值不会无意间变动,应该在设置新属性值时拷贝一份,保护其封装性 block也经常使用copy关键字 block 使用 copy 是从 MRC 遗留下来的"传统",在 MRC 中,方法内部的 block 是在栈区的,使用 copy 可以把它放到堆区. 在ARC

strong ,weak,copy关键字使用,及weak跟assign区别

1strong和weak都是针对对象 2strong持有对象的所有权 3weak不持有对象的所有权 什么时候使用weak 1.委托 为了规避循环引用 使用weak限定delegate指针 2.SB拖拽控件 可以使用weak关键字限定属性 与assign的区别 1.weak只能修饰对象指针,assign可以修饰任意类型指针 2.weak指针指向的对象被释放 weak指针自动置为nil,assign不会自动置为nil copy关键字如何使用 1.block 在栈区分配内存(系统管理其内存) 拷贝到堆

OC中NSFileManager类 和 copy一些用法

一:NSFileManager的使用 1, 概念:用来管理文件系统,它可以用来进行常见的文件\文件夹的操作(拷贝.剪切.创建等) NSFileManager 使用了单例模式singleton 使用defaultManager方法可以获取那个单例对象 2, 1>常见判断 //1,判断文件或文件夹是否存放 // NSFileManager *fileManager = [NSFileManager defaultManager]; // NSString *path = @"/Users/ll

【学习笔记】【oc】copy与mutableCopy

copy 返回一个不可变的对象: mutableCopy 返回一个可变的对象: 使用copy方法时 类必须实现:<NSCopying>协议中的-(id)copyWithZone:(NSZone*)zone方法 -(id)copyWithZone: { FKDog* dog=[[[self class]allocWithZone:zone]init]; dog.name=self.name; dog.age=self.age return dog; } 子类重写父类的该方法时,必须 -(id)c

Objective-C copy(转)

一.从面向对象到Objective-C概览copy 1.面向对象: In object-oriented programming, object copying is creating a copy of an existing object, a unit of data in object-oriented programming. The resulting object is called an object copy or simply copy of the original obj

iOS copy/retain/assign

1 深复制:内容拷贝,源对象和副本对象指的是两个不同的对象,源对象引用计数器不变,副本对象引用计数器为1 2 浅复制:指针拷贝,源对象和副本对象指的都是同一个对象,对象引用计数器+1,相当于retain 3 只有不可变对象创建不可变副本(copy)才是浅复制,其它的都是深复制 copy关键字的使用 平时我们使用对象之间的传值都是采用retain count +1的方式,这种方式的适用于当对象的某属性的值改变时,引用该对象的不同指针会同时改变,因为这两个指针指向的是同一个内存地址, 但如果需求是,

OC中的copy

copy的概念 1 Copy的字面意思是"复制"."拷贝",是一个产生副本的过程 2 对象拷贝的目的:要使用某个对象的数据,但是在修改对象的时候不影响原来的对象内容,常见的复制有用一个源文件产生:文件复制 3 作用:复制一个副本文件 特点: 1 修改源文件的内容,不会影响副本文件 2 修改副本文件的内容,不会影响源文件 3 OC中的copy,就是指的对象的拷贝 4 作用:利用一个源对象产生一个副本对象 5 6 修改源对象的属性和行为,不会影响副本对象 7 修改副本对

Objective-C(十六、内存管理,自动释放池,ARC,强指针,弱指针,方法族)——iOS开发基础

结合之前的学习笔记以及参考<Objective-C编程全解(第三版)>,对Objective-C知识点进行梳理总结.知识点一直在变,只是作为参考,以苹果官方文档为准~ 十六.内存管理相关知识(二) 1.autorelease,自动释放机制 - (instancetype)autorelease; (1)自动释放池的创建 iOS5.0之前 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; //进行一系列操作 //此处不可以使用

OC基础(26)

集合对象的内存管理 Copy copy与内存管理 @property中的copy关键字 自定义的类实现copy操作 *:first-child { margin-top: 0 !important; } body > *:last-child { margin-bottom: 0 !important; } a { color: #4183C4; } a.absent { color: #cc0000; } a.anchor { display: block; padding-left: 30p