iOS之strong和copy

深拷贝和浅拷贝

深拷贝和浅拷贝主要是对类类型而言的,浅拷贝就是指针拷贝,深拷贝是对象拷贝。

property的strong和copy

在接触iOS程序时经常会看到程序某些类类型属性被strong修饰,某些被copy修饰,刚开始接触时有些疑惑,后来不知咋地形成了这样的观念,对于mutable类型,用copy修饰,对于immutable类型,用strong修饰,现在看来这个大大地错了。

创建属性是一件机械化的工作:对于一般的属性,你会将它们声明为 nonatomic。默认情况下,对象属性是strong的,标量属性是assign的。但是有一个例外,就是对于具有可变副本的属性,我们倾向于将其声明为 copy。比如说,name属性的类型是NSString,有可能有人创建了一个Person对象,并且给这个属性赋了一个NSMutableString的名字值。然后过了一会儿,这个可变字符串被变更了。如果我们的属性不是copy而是strong的话,随着可变字符串的改变,我们的Person对象也将发生改变,这不是我们希望发生的。对于类似数组或者字典这样的容器类来说,也是这样的情况。

简单来说,对于被strong修饰property,在对其赋值时,并不是真正拷贝,而只是将右值的retain count加1,将左值的指针改为右值(也是一个指针)所指向的地址,直观来看,即赋值后左值和右值的地址值是一样一样的,即浅拷贝;
但对于被copy所修饰的property且它遵循NSCopying协议,在为property赋值时,赋值语句不会保留新值(变量不会随可变字符串的改变而改变),直观来看,赋值后左值和右值的地址值可能不同,也即“赋值语句可能是深拷贝,也可能是浅拷贝”;
P.S:这里为什么说“可能”呢?下文会对这个问题进行阐述。
P.S:对于属性的赋值,单纯理解为浅拷贝或深拷贝操作是不对的,因为还会改变retain count的值(对于浅拷贝而言)。

举个栗子:

// 两个属性,前者用copy修饰,后者用strong修饰
@property (nonatomic, copy) NSString *firstName;
@property (nonatomic, strong) NSString *lastName; NSMutableString *str1 = [NSMutableString stringWithString:@"不坏"];
self.firstName = str1; // 赋值
// 打印左值和右值的地址值
NSLog(@"str1 addr: 0x%lx", str1);
NSLog(@"self.first addr: 0x%lx", self.firstName); NSLog(@""); NSMutableString *str2 = [NSMutableString stringWithString:@"张"];
self.lastName = str2; // 赋值
// 打印左值和右值的地址值
NSLog(@"str2 addr: 0x%lx", str2);
NSLog(@"lastName addr: 0x%lx", self.lastName);

打印结果是:

str1 addr: 0x7967f580
self.first addr: 0x7967f1d0 str2 addr: 0x7967f460
lastName addr: 0x7967f460

显然,对于用copy修饰的firstName,其赋值所采用的方式是深拷贝;对于用strong修饰的lastName,赋值方式是浅拷贝。

回过头来解释上述的“可能”。

其实很简单,对于赋值语句self.firstName = str1;(firstName被copy修饰),如果右值str1是mutable,则这条语句处理的方式是深拷贝。因为如果str1是mutable,并且执行浅拷贝的话,这就意味着如果str1改变了,则self.firstName也会跟着改变,这是我们不希望的,所以执行深拷贝时合理的;如果右值str1是immutable,则赋值语句所处理的方式依然是浅拷贝,因为反正str1不会再发生改变了,对self.firstName值不会有意想不到的影响,此时进行深拷贝有些浪费。

P.S:Objective-C的这种设计理念略显啰嗦,比较智能的处理方法是当str1改变时,彼时再进行深拷贝处理,很多现代语言都是这么弄的,譬如Python。

总结:property的关键字copy的本意是为了保护“immutable属性在被mutable右值对象赋值后不被右值之后可能发生的改变所影响”,而对mutable属性,则不存在这样的问题。

上一篇:iOS 关键词assign、strong、copy、weak、unsafe_unretained


下一篇:【转】Visual Studio团队资源管理器 Git 源码管理工具简单入门