copy和mutableCopy

一、copy和mutableCopy

NSObject类有两个跟拷贝相关的方法:copy和mutableCopy。这两个方法都是返回一个id类型的对象。

  • ①、copy方法:返回copyWithZone方法返回的对象(Returns the object returned by copyWithZone:);
  • ②、mutableCopy方法:返回mutableCopyWithZone方法返回的对象(Returns the object returned by mutableCopyWithZone:);

二、NSString对象调用copy和mutableCopy

  • 1、先定义如下通用宏:
//打印方法名
#define LOG_METHOD_NAME {NSLog(@"%@", NSStringFromSelector(_cmd));}
//打印对象的类名,以及对象本身的地址
#define LOG_OBJ_ADDRESS(obj) {NSLog(@"%@ : %p",NSStringFromClass([obj class]), obj);}
//打印空行
#define LOG_END {NSLog(@"%@", @" ");}
  • 2、测试代码如下:
- (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

二、NSMutableString对象调用copy和mutableCopy

  • 1、测试代码如下:
- (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返回一个可变对象(新的);

  • 针对可变对象调用copy返回一个不可变对象(新的),调用mutableCopy返回另外一个可变对象(新的)。

class copy mutableCopy
不可变(如,NSString) 返回本身(相当于retain一次) 创建新的可变对象(如,创建一个NSMutableString对象,自制跟远对象不同)
可变(如,NSMutableString) 创建新的不可变对象(如,创建一个NSTaggedPointerString对象,地址跟原对象不同) 创建新的可变对象(如,创建一个NSMutableString对象,地址跟远对象不同)

四、属性copy还是strong

  • 1、假设定义两个id类型如下
@property (nonatomic, copy) id cpID;
@property (nonatomic, strong) id stID;
  • 2、那么编译器把以上两属性分别实现为:
@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版本。

五、NSString的属性是copy还是strong

  • 1、测试代码如下:
@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的返回值;

六、NSMutableString的属性是copy还是strong

  • 1、测试代码如下:
@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谈起

你可能感兴趣的:(copy,mutCopy)