copy协议解析
在日常的 iOS
开发中,我们经常会用到如下代码:
1 | @property (nonatomic,copy) NSString * name; |
很多人告诉你只要是 NSString
类型你就必须给我用 copy
来声明。但是如果我告诉你很多时候 NSDictionary
或者 NSArray
等等也有可能用 copy
来修饰的话,你是不是会独自混乱一会儿?所以如果你想知道这其中的原理请往下看,如果你只是停留在简单的使用层面,那么请去其他地方看更有意义的事情吧。
首先要说的是,使用 copy
的目的很简单,就是复制一个对象本身,而不是创建一个指向这个对象的指针。那么我们来看看如果上面的代码使用strong会发生什么事情。
1 | @property (nonatomic,strong) NSString * name; |
在主函数中我们编写如下代码:
1 | NSMutableString * str = [NSMutableString stringWithString:@"Tom"]; |
请注意这里我不使用 NSString
而使用 NSMutableString
是因为像 @“123”
这样的字符串苹果为了性能考虑都变成了常量,并且使用 NSMutableString
我可以有效的利用字符串的改变来描述使用 strong
与 copy
的不同。
上述代码中,变量 str
经过追加字符串操作后显然应该变成 @"Tom and jack"
,让我们来看一下运行结果:
细心的同学可以一下子就发现问题: str
和 self.name
竟然输出的是一样的,但是貌似我们并没有改变 self.name
。这就说明 str
和 self.name
指向的是同一块内存地址,这种情况在实际的开发中存在很多隐患。如果我们使用的是 copy
,输出的结果如下图:
所以当我们需要保证属性是独立复制出来的一个新对象的时候,请使用 copy
,如果只是简单的指针传递,请使用 strong
。
直到现在我并没有提及到 copy
协议的任何东西,不要着急,下面我们来自己定义一个类。
1 | @interface Animal : NSObject |
这个类继承自 NSObject
并仅有一个属性 name
。这时我们在主控制器中做一个 Animal
的属性,并且我们希望它是 copy
的,并编写主函数代码。
1 | @property (nonatomic,copy) Animal * dog; |
让我们直接运行:
很不幸,程序崩溃了,崩溃的原因写的很清楚 Animal
类缺少一个方法 copyWithZone:
,这个方法就是我们要说的 copy
协议方法,让我们来看看官方头文件的说明:
1 | @protocol NSCopying |
遵守 NSCopying
协议可以使用 [anyObejct copy]
这样的方法来拷贝一个对象,遵守 NSMutableCopying
可以使用 [anyObejct mutableCopy]
来拷贝出一个可变对象。我们暂时先看 NSCopying
协议,在 Animal
中我们补充这个协议方法:
1 | @interface Animal : NSObject<NSCopying> |
再次运行:
结果显示正确,这说明系统的很多类如 NSArray
、 NSDictionary
、 NSString
等都默认实现了 copy
协议,而我们自己定义的类如果想要拷贝功能就必须自己实现它。
现在我们来看 NSMutableCopying
协议,顾名思义我们利用这个协议可以得到一个可变拷贝对象,如果你之前对 NSArray
与 NSMutableArray
了如指掌的话我就不用说太多了,直接看代码。
1 | @property (nonatomic,copy) NSMutableArray * testArr; |
结果如下:
但是当我们改变 self.testArr
时
1 | [self.testArr setObject:@"123" atIndexedSubscript:0]; |
会崩溃掉,此时我们输出 self.testArr
看看:
我们从一个 NSMutableArray
赋值过来的属性竟然是 NSArray
类型的,那么我们很自然的会想到 NSMutableCopying
协议,下面我们来重写 self.testArr
的 setter
方法。
1 | - (void)setTestArr:(NSMutableArray *)testArr |
再次运行:
程序没有崩溃,并且得到了一个 NSMutableArray
类型的属性,并成功改变了数组的第一个元素。
以上是我对 copy
协议的一点拙见,代码我就不贴了,大家敲敲练习一下。
good luck!