从copy和mutableCopy谈起
从copy和mutableCopy谈起
Copy和mutableCopy
NSObject类有两个跟拷贝相关的方法——copy和mutableCopy。这两个方法都是返回一个id类型的对象,那么这两者之间有什么区别呢?根据官方文档解释,copy方法,返回copyWithZone方法返回的对象(Returns the object returned by copyWithZone:)。而mutableCopy方法,返回mutableCopyWithZone方法返回的对象(Returns the object returned by mutableCopyWithZone:)。读起来有点绕,一言以蔽之,调用copy就是调用copyWithZone,调用mutableCopy就是调用mutableCopyWithZone。还是不够清楚!!!接下来我们以NSString为例子,来说明copy和mutableCopy的区别。
NSString对象调用copy和mutableCopy
NSString *string = @"a";
NSString *stringCopy = [string copy];
NSMutableString *stringMCopy = [string mutableCopy];
NSLog(@"string:%@ -- %p",[string class],string);
NSLog(@"stringCopy:%@ -- %p",[stringCopy class],stringCopy);
NSLog(@"stringMCopy:%@ -- %p",[stringMCopy class],stringMCopy);
打印结果如下:
2017-05-22 12:24:23.175 Copy&MutableCopy[24729:5637322] string:__NSCFConstantString -- 0x10d24d068
2017-05-22 12:24:23.176 Copy&MutableCopy[24729:5637322] stringCopy:__NSCFConstantString -- 0x10d24d068
2017-05-22 12:24:23.176 Copy&MutableCopy[24729:5637322] stringMCopy:__NSCFString -- 0x608000078800
由以上输出可知,对一个NSString对象调用copy返回的还是该对象本身,因为str的地址和cpStr的地址是同一个。而调用mutableCopy,返回的是一个NSMutableString对象(注:__NSCFConstantString是常量串即NSString,而__NSCFString是可变串即NSMutableString)。
NSMutableString对象调用copy和mutableCopy
NSMutableString *mString = [NSMutableString stringWithString:@"b"];
NSString *mstringCopy = [mString copy];
NSMutableString *mstringMCopy = [mString mutableCopy];
NSLog(@"mString:%@ -- %p",[mString class],mString);
NSLog(@"mstringCopy:%@ -- %p",[mstringCopy class],mstringCopy);
NSLog(@"mstringMCopy:%@ -- %p",[mstringMCopy class],mstringMCopy);
打印结果如下:
2017-05-22 12:24:23.176 Copy&MutableCopy[24729:5637322] mString:__NSCFString -- 0x608000078b00
2017-05-22 12:24:23.176 Copy&MutableCopy[24729:5637322] mstringCopy:NSTaggedPointerString -- 0xa000000000000621
2017-05-22 12:24:23.176 Copy&MutableCopy[24729:5637322] mstringMCopy:__NSCFString -- 0x608000078ac0
由以上输出可知,对一个NSMutableString对象调用copy返回的是一个NSTaggedPointerString对象,该对象可认为是一个常量串。而调用mutableCopy返回的是另外一个可变对象__NSCFString,即NSMutableString(原NSMutableString对象的地址是0x7fabba9012c0,新NSMutableString对象地址是0x7fabb840a860)。
针对NSArray、NSDictionary、NSSet等具有Mutable版本的类进行试验出现跟NSString类似的现象,不一一列举,有兴趣可以自己去试验。
copy和mutableCopy调用小结
class | copy | mutablecopy |
---|---|---|
不可变(如,NSString) | 返回本身(相当于retain一次) | 创建新的可变对象(如,创建一个NSMutableString对象,地址跟原对象不同) |
可变(如,NSMutableString) | 创建新的不可变对象(如,创建一个NSTaggedPointerString对象,地址跟原对象不同 ) | 创建新的可变对象(如,创建一个NSMutableString对象,地址跟原对象不同 ) |
- 针对不可变对象调用copy返回该对象本身,调用mutableCopy返回一个可变对象(新的);
- 针对可变对象调用copy返回一个不可变对象(新的),调用mutableCopy返回另外一个可变对象(新的)。
再进一步从是否新建返回对象,返回对象是否可变两个角度总结如下:
- 只有不可变的copy是retain一次,其他都是创建一个新对象;
- copy返回的是不可变对象,mutableCopy返回的是可变对象。
属性copy、strong
先来看下测试代码
@property (nonatomic, strong) NSString *strongStr;
@property (nonatomic, copy) NSString *copStr;
@property (nonatomic, strong) NSMutableString *mStrongStr;
@property (nonatomic, copy) NSMutableString *mCopStr;
NSString *str = @"test";
NSLog(@"str: %@ -- %p",str,str);
self.strongStr = str;
NSLog(@"strongStr: %@ -- %p",_strongStr,_strongStr);
self.copStr= str;
NSLog(@"copStr: %@ -- %p",_copStr,_copStr);
// self.mStrongStr = str;
// NSLog(@"mStrongStr:%@ -- %p",_mStrongStr,_mStrongStr);
// self.mCopStr= str;
// NSLog(@"mCopStr:%@ -- %p",_mCopStr,_mCopStr);
str = @"aaa";
NSLog(@"str: %@ -- %p",str,str);
NSLog(@"strongStr: %@ -- %p",_strongStr,_strongStr);
NSLog(@"copStr: %@ -- %p",_copStr,_copStr);
NSMutableString *str2 = [NSMutableString stringWithString:@"test2"];
NSLog(@"str2: %@ -- %p",str2,str2);
self.strongStr = str2;
NSLog(@"strongStr: %@ -- %p",_strongStr,_strongStr);
self.copStr= str2;
NSLog(@"copStr: %@ -- %p",_copStr,_copStr);
self.mStrongStr = str2;
NSLog(@"mStrongStr: %@ -- %p",_mStrongStr,_mStrongStr);
self.mCopStr= str2;
NSLog(@"mCopStr: %@ - %p",_mCopStr,_mCopStr);
[str2 appendString:@"bbb"];
NSLog(@"str2: %@ -- %p",str2,str2);
NSLog(@"strongStr: %@ -- %p",_strongStr,_strongStr);
NSLog(@"copStr: %@ -- %p",_copStr,_copStr);
NSLog(@"mStrongStr: %@ -- %p",_mStrongStr,_mStrongStr);
NSLog(@"mCopStr: %@ -- %p",_mCopStr,_mCopStr);
2017-05-22 12:57:35.662 Copy&MutableCopy[25257:5764440] str: test -- 0x10bf20078
2017-05-22 12:57:35.663 Copy&MutableCopy[25257:5764440] strongStr: test -- 0x10bf20078
2017-05-22 12:57:35.663 Copy&MutableCopy[25257:5764440] copStr: test -- 0x10bf20078
2017-05-22 12:57:35.663 Copy&MutableCopy[25257:5764440] str修改后: aaa -- 0x10bf200f8
2017-05-22 12:57:35.663 Copy&MutableCopy[25257:5764440] strongStr修改后: test -- 0x10bf20078
2017-05-22 12:57:35.663 Copy&MutableCopy[25257:5764440] copStr修改后: test -- 0x10bf20078
//由以上输出可知,假设两个NSString属性实际上指向的都是一个NSString对象,那么在原NSString对象修改后,strong版本的NSString和copy版本属性都保持原状。
2017-05-22 12:57:35.663 Copy&MutableCopy[25257:5764440] str2: test2 -- 0x608000262400
2017-05-22 12:57:35.663 Copy&MutableCopy[25257:5764440] strongStr: test2 -- 0x608000262400
2017-05-22 12:57:35.663 Copy&MutableCopy[25257:5764440] copStr: test2 -- 0xa000032747365745
2017-05-22 12:57:35.664 Copy&MutableCopy[25257:5764440] mStrongStr: test2 -- 0x608000262400
2017-05-22 12:57:35.664 Copy&MutableCopy[25257:5764440] mCopStr: test2 - 0xa000032747365745
2017-05-22 12:57:35.664 Copy&MutableCopy[25257:5764440] str2修改后: test2bbb -- 0x608000262400
2017-05-22 12:57:35.664 Copy&MutableCopy[25257:5764440] strongStr修改后: test2bbb -- 0x608000262400
2017-05-22 12:57:35.664 Copy&MutableCopy[25257:5764440] copStr修改后: test2 -- 0xa000032747365745
2017-05-22 12:57:35.664 Copy&MutableCopy[25257:5764440] mStrongStr修改后: test2bbb -- 0x608000262400
2017-05-22 12:57:35.664 Copy&MutableCopy[25257:5764440] mCopStr修改后: test2 -- 0xa000032747365745
//由以上输出可知,假设两个NSString属性实际上指向的都是一个NSMutableString对象,那么在原NSMutableString对象修改后,strong版本的NSString属性跟着修改,而copy版本属性保持原状。self.cpStr实际上是一个NSTaggedPointerString对象,该对象正是NSMutableString对象执行copy的返回值
如果
[self.mCopStr appendString:@"bbb"];
报错:
[NSTaggedPointerString appendString:]: unrecognized selector sent to instance 0xa000032747365745
原因很明显,是朝NSTaggedPointerString对象发了一个它不能识别的selector。原因是copy版本的NSMutableString属性本质上不是一个NSMutableString对象,而是一个NSTaggedPointerString对象,它是一个不可变对象。该对象是NSMutableString对象执行copy得来的,还记得我们上一节的结论吗?对一个对象执行copy得到的用于是一个不可变的对象。
针对NSArray、NSDictionary、NSSet等具有Mutable版本的类进行试验出现跟NSString类似的现象。
结论
- 不可变类型属性,推荐使用copy,因为假设该对象实际上指向的是一个mutable的对象,mutable对象的改变不会导致该对象的改变;假设指向的不是mutable的对象,那么copy和strong是等价,都是执行一次retain。
- 可变类型属性,不能使用copy,因为copy产生的对象是一个不可变对象,跟属性描述是冲突的。
原文地址:http://www.cocoachina.com/ios/20151202/14520.html