NSObject类有两个跟拷贝相关的方法:copy和mutableCopy。这两个方法都是返回一个id类型的对象。
//打印方法名
#define LOG_METHOD_NAME {NSLog(@"%@", NSStringFromSelector(_cmd));}
//打印对象的类名,以及对象本身的地址
#define LOG_OBJ_ADDRESS(obj) {NSLog(@"%@ : %p",NSStringFromClass([obj class]), obj);}
//打印空行
#define LOG_END {NSLog(@"%@", @" ");}
- (void)stringCopyTest{
LOG_METHOD_NAME
NSString *str = @"Hello";
LOG_OBJ_ADDRESS(str);
NSString *cpStr = [str copy];
LOG_OBJ_ADDRESS(cpStr);
NSMutableString *mutCpStr = [str mutableCopy];
LOG_OBJ_ADDRESS(mutCpStr);
LOG_END
}
3、打印如下:
2015-12-22 15:14:33.416 toast显示[53390:2099784] stringCopyTest
2015-12-22 15:14:33.416 toast显示[53390:2099784] __NSCFConstantString : 0x10aed26b0
2015-12-22 15:14:33.416 toast显示[53390:2099784] __NSCFConstantString : 0x10aed26b0
2015-12-22 15:14:33.416 toast显示[53390:2099784] __NSCFString : 0x7fe071c1ee40
2015-12-22 15:14:33.416 toast显示[53390:2099784]
4、总结
由以上输出可知,对一个NSString对象调用copy返回的还是该对象本身,因为str的地址和cpStr的地址是用一个。而调用mutableCopy,返回的是一个NSMutableString对象。
注:__NSCFConstantString是常量串即NSString,而__NSCFString是可变串即NSMutableString
- (void)mutableStringCopyTest{
LOG_METHOD_NAME
NSMutableString *mutStr = [@"OC" mutableCopy];
LOG_OBJ_ADDRESS(mutStr);
NSMutableString *cpMutStr = [mutStr copy];
LOG_OBJ_ADDRESS(cpMutStr);
NSMutableString *mutCpMutStr = [mutStr mutableCopy];
LOG_OBJ_ADDRESS(mutCpMutStr);
LOG_END
}
2、打印如下:
2015-12-22 15:20:06.162 toast显示[53437:2103898] mutableStringCopyTest
2015-12-22 15:20:06.163 toast显示[53437:2103898] __NSCFString : 0x7fe4b1d133f0
2015-12-22 15:20:06.163 toast显示[53437:2103898] NSTaggedPointerString : 0xa0000000000434f2
2015-12-22 15:20:06.163 toast显示[53437:2103898] __NSCFString : 0x7fe4b1c033a0
2015-12-22 15:20:06.163 toast显示[53437:2103898]
3、总结
由以上输出可知,对一个NSMutableString对象调用copy返回的是一个NSTaggedPointerString对象,该对象可认为是一个常量串。而调用mutableCopy返回的是另外一个可变对象__NSCFString,即NSMutableString(原NSMutableString对象的地址是0x7fe4b1d133f0,新NSMutableString的对象地址是0x7fe4b1c033a0)。
PS:针对NSArray、NSDictionary、NSSet等具有Mutable版本的类进行实验出现跟NSString类似的现象;
针对不可变对象调用copy返回该对象本身,调用mutableCopy返回一个可变对象(新的);
针对可变对象调用copy返回一个不可变对象(新的),调用mutableCopy返回另外一个可变对象(新的)。
class | copy | mutableCopy |
---|---|---|
不可变(如,NSString) | 返回本身(相当于retain一次) | 创建新的可变对象(如,创建一个NSMutableString对象,自制跟远对象不同) |
可变(如,NSMutableString) | 创建新的不可变对象(如,创建一个NSTaggedPointerString对象,地址跟原对象不同) | 创建新的可变对象(如,创建一个NSMutableString对象,地址跟远对象不同) |
@property (nonatomic, copy) id cpID;
@property (nonatomic, strong) id stID;
@synthesize cpID = _cpID;
@synthesize stID = _stID;
- (void)setCpID:(id)cpID{
_cpID = [cpID copy];
}
- (id)cpID{
return _cpID;
}
- (void)setStID:(id)stID{
_stID = stID;
}
- (id)stID{
return _stID;
}
3、结论:
从以上实现可以看出,strong和copy的属性主要是set方法有区别,strong的set是直接设置指定值,而copy的set是设置指定值的copy版本。
@interface ViewController ()
@property (nonatomic, copy) NSString *cpStr;
@property (nonatomic, strong) NSString *stStr;
@end
@implementation ViewController
- (void)stringPropertyTest
{
LOG_METHOD_NAME;
NSMutableString* mutStr = [@"123" mutableCopy];
LOG_OBJ_ADDRESS(mutStr);
self.cpStr = mutStr;
LOG_OBJ_ADDRESS(self.cpStr);
self.stStr = mutStr;
LOG_OBJ_ADDRESS(self.stStr);
NSLog(@"修改前");
NSLog(@"mutStr:%@", mutStr);
NSLog(@"copy:%@", self.cpStr);
NSLog(@"strong:%@", self.stStr);
[mutStr appendString:@"456"];
NSLog(@"修改后");
NSLog(@"mutStr:%@", mutStr);
NSLog(@"copy:%@", self.cpStr);
NSLog(@"strong:%@", self.stStr);
LOG_END
}
2、打印结果如下:
2015-12-29 00:13:13.630 toast显示[10615:514242] stringPropertyTest
2015-12-29 00:13:13.630 toast显示[10615:514242] __NSCFString : 0x7fd63851f6a0
2015-12-29 00:13:13.630 toast显示[10615:514242] NSTaggedPointerString : 0xa000000003332313
2015-12-29 00:13:13.630 toast显示[10615:514242] __NSCFString : 0x7fd63851f6a0
2015-12-29 00:13:13.630 toast显示[10615:514242] 修改前
2015-12-29 00:13:13.630 toast显示[10615:514242] mutStr:123
2015-12-29 00:13:13.630 toast显示[10615:514242] copy:123
2015-12-29 00:13:13.631 toast显示[10615:514242] strong:123
2015-12-29 00:13:13.631 toast显示[10615:514242] 修改后
2015-12-29 00:13:13.631 toast显示[10615:514242] mutStr:123456
2015-12-29 00:13:13.631 toast显示[10615:514242] copy:123
2015-12-29 00:13:13.632 toast显示[10615:514242] strong:123456
2015-12-29 00:13:13.632 toast显示[10615:514242]
3、结论:
由以上输出可知,假设两个NSString属性实际上指向的都是一个NSMutableString对象,那么在原NSMutableString对象修改后,strong版本的NSString属相跟着修改,而copy版本属性保持原状。self.cpStr实际上是一个NSTaggedPointerString对象,该对象正是NSMutableString对象执行copy的返回值;
@interface ViewController ()
@property (nonatomic, copy) NSMutableString *cpMutStr;
@property (nonatomic, strong) NSMutableString *stMutStr;
@end
@implementation ViewController
- (void)stringPropertyTest
{
LOG_METHOD_NAME;
NSMutableString* mutStr = [@"123" mutableCopy];
LOG_OBJ_ADDRESS(mutStr);
self.cpMutStr = mutStr;
LOG_OBJ_ADDRESS(self.cpMutStr);
self.stMutStr = mutStr;
LOG_OBJ_ADDRESS(self.stMutStr);
NSLog(@"修改前");
NSLog(@"mutStr:%@", mutStr);
NSLog(@"copy:%@", self.cpMutStr);
NSLog(@"strong:%@", self.stMutStr);
[mutStr appendString:@"456"];
NSLog(@"修改后");
NSLog(@"mutStr:%@", mutStr);
NSLog(@"copy:%@", self.cpMutStr);
NSLog(@"strong:%@", self.stMutStr);
LOG_END
[self.cpMutStr appendString:@"789"];
}
2、打印结果如下:
2015-12-29 00:31:24.807 toast显示[570:9278] stringPropertyTest
2015-12-29 00:31:24.807 toast显示[570:9278] __NSCFString : 0x7fec70c20c60
2015-12-29 00:31:24.807 toast显示[570:9278] __NSCFString : 0x7fec70c3e950
2015-12-29 00:31:24.807 toast显示[570:9278] __NSCFString : 0x7fec70c20c60
2015-12-29 00:31:24.807 toast显示[570:9278] 修改前
2015-12-29 00:31:24.807 toast显示[570:9278] mutStr:123
2015-12-29 00:31:24.808 toast显示[570:9278] copy:123
2015-12-29 00:31:24.808 toast显示[570:9278] strong:123
2015-12-29 00:31:24.808 toast显示[570:9278] 修改后
2015-12-29 00:31:24.808 toast显示[570:9278] mutStr:123456
2015-12-29 00:31:24.808 toast显示[570:9278] copy:123
2015-12-29 00:31:24.808 toast显示[570:9278] strong:123456
2015-12-29 00:31:24.808 toast显示[570:9278]
3、结论:
看起来没啥问题,strong版本的属性跟随原对象的变化而变化,copy版本的属性不变。但是,假设调用
[self.cpMutStr appendString:@"789"];
程序会崩溃。崩溃信息如下:
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[NSTaggedPointerString appendString:]: unrecognized selector sent to instance 0xa000000003332313'*** First throw call stack:
原因很明显,是朝NSTaggedPointerString对象发了一个它不能识别的selector。原因是copy版本的NSMutableString属性本质上不是一个NSMutableString对象,而是一个NSTaggedPointerString对象,它是一个不可变对象。该对象是NSMutableString对象执行copy得来的,还记得我们上一节的结论吗?对一个对象执行copy得到的用于是一个不可变的对象。
PS:针对NSArray、NSDictionary、NSSet等具有Mutable版本的类进行试验出现跟NSString类似的现象。
本文来至从copy和mutableCopy谈起