iOS属性修饰符的使用场景及区别联系-copy


copy

    在iOS开发中,对象之间传值都是使用引用计数器增加的方式,这种方式的适用于当对象的某属性的值改变时,引用该对象的不同指针会同时改变,因为这两个指针指向的是同一个内存地址,当一个指针执行的对象属性值发生改变时,不影响另一个对象,那么需要分配两个不同的内存地址,也就是说,我们就不可以采用retain关键字了,而是要采用copy 关键字。如:

   浅拷贝就是用copy,深拷贝就是用mutableCopy。如果有这样的误解,一定要更正过来。copy只是不可变拷贝,而mutableCopy是可变拷贝。比如,NSArray *arr = [modelsArray copy],那么arr是不可变的。而NSMutableArray *ma = [modelsArray mutableCopy],那么ma是可变的。

lili.array = [@[@"谢谢", @"感谢"] mutableCopy];
NSMutableArray *otherArray = [lili.array copy];
lili.array[0] = @"修改了谢谢";
// 打印: 谢谢 修改了谢谢
// 说明数组里面是字符串时,直接使用copy是相当于深拷贝的。
NSLog(@"%@ %@", otherArray[0], lili.array[0]);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

这里就是浅拷贝,但是由于数组中的元素都是字符串,因此不会影响原来的值。

数组中是对象时:

NSMutableArray *personArray = [[NSMutableArray alloc] init];
Person *person1 = [[Person alloc] init];
person1.name = @"lili";
[personArray addObject:person1];

Person *person2 = [[Person alloc] init];
person2.name = @"lisa";
[personArray addObject:person2];

// 浅拷贝
NSArray *newArray = [personArray copy];
Person *p = newArray[0];
p.name = @"lili的名字被修改了";

// 打印结果:lili的名字被修改了
// 说明这边修改了,原来的数组对象的值也被修改了。虽然newArray和personArray不是同一个数组,不是同一块内存,
// 但是实际上两个数组的元素都是指向同一块内存。
NSLog(@"%@", ((Person *)(personArray[0])).name);
深拷贝,其实就是对数组中的所有对象都创建一个新的对象:

NSMutableArray *personArray = [[NSMutableArray alloc] init];
Person *person1 = [[Person alloc] init];
person1.name = @"lili";
[personArray addObject:person1];

Person *person2 = [[Person alloc] init];
person2.name = @"lisa";
[personArray addObject:person2];

// 深拷贝
NSMutableArray *newArray = [[NSMutableArray alloc] init];
for (Person *p in personArray) {
Person *newPerson = [[Person alloc] init];
newPerson.name = p.name;

[newArray addObject:newPerson];
}
Person *p = newArray[0];
p.name = @"lili的名字被修改了";

// 打印结果:lili
NSLog(@"%@", ((Person *)(personArray[0])).name);

总结:

 区分深复制与浅复制,一般只有可变的mutableCopy到mutableCopy的Copy才是浅复制,不产生副本只是retain count增加。
副本的特点:彼此的内容一样,拥有相同的方法,在内存中有两个对象.
1.copy
1> 如果对象有 可变/不可变 版本的区别, copy方法只能拷贝出不可变的副本.
2> 如果对象没有 可变/不可变 版本的区别,copy方法只是建立一个副本.

2.mutableCopy
建立对象的可变版本的副本(如果对象有 可变/不可变 版本之分)

3.深拷贝/浅拷贝
1>深拷贝:如果建立出新的副本,在内存中有两个对象.就是深拷贝.
可变 -> 不可变 (copy)  
可变 -> 可变 (mutableCopy)
不可变 -> 可变 (mutableCopy)

2.浅拷贝:不会建立新的副本, 只是引用计数加1.
不可变 -> 不可变  (因为两个不可变对象谁都不会被改变,也就没必要建立副本)

场景

block声明使用copy


在使用block时,尽量使用typedef来起一个别名,这样更容易阅读。使block作为属性时,使用copy。

typedef void (^HYBTestBlock)(NSString *name);

@property (nonatomic, copy) HYBTestBlock testBlock;
  • 1
  • 2
  • 3

字符串使用copy


对于字符串,通常都是使用copy的方式。虽然使用strong似乎也没有没有问题,但是事实上在开发中都会使用copy。为什么这么做?因为对于字符串,我们希望是一次内容的拷贝,外部修改也不会影响我们的原来的值,而且NSString类遵守了NSCopying, NSMutableCopying, NSSecureCoding协议。

下面时使用copy的方式,验证如下:

NSString *hahaString = @"哈哈";
NSString *heheString = [hahaString copy];
// 哈哈, 哈哈
NSLog(@"%@, %@", hahaString, heheString);
heheString = @"呵呵";
// 哈哈, 呵呵
NSLog(@"%@, %@", hahaString, heheString);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

我们修改了heheString,并不会影响到原来的hahaString。copy一个对象变成新的对象(新内存地址) 引用计数为1 原来对象计数不变。


总结;

copy参数的作用:
在属性的 setter 实现中对赋值对象做一次copy操作,将copy操作的结果赋值给属性.

具体情况如下:

1.情况一: 属性是不可变类型的. 如 NSString
如果赋值对象是可变的,那么会将一个不可变副本赋值给属性.
如果赋值对象是不可变的,那么不会产生新的副本,只是对复制对象引用计数加1.

2.情况二. 属性是可变类型的, 建议不要是用 copy 参数
因为可变类型的属性,我们是会根据需求对其内容进行修改的,如果用copy那么,该属性保存的真是对象类型就是不可变的,这样如果去修改这个属性,编译时不会报错,但是运行会奔溃,因为尝试去修改一个不可变类型的对象.

3.情况三. 自定义对象类型, 一般情况下我们不会对自定义的对象使用 copy 参数.
必须遵守<NSCopying>协议 ,实现 copyWithZone: 方法. 才能够调用 copy 方法.建立副本



阅读更多
文章标签: iOS copy 属性修饰符
个人分类: iOS基础
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

不良信息举报

iOS属性修饰符的使用场景及区别联系-copy

最多只允许输入30个字

加入CSDN,享受更精准的内容推荐,与500万程序员共同成长!
关闭
关闭