OC NSString在@property中strong和copy的区别
定义一个strong修饰的字符串属性testString1
定义一个copy修饰的字符串属性testString2
#import "ViewController.h"
@interface ViewController ()
@property (nonatomic, strong) NSString *testString1;
@property (nonatomic, copy) NSString *testString2;
@end
**
测试代码
**
// 浅复制1
- (IBAction)shallowCopy1Action:(id)sender {
NSString *string = [NSString stringWithFormat:@"MyTest"];
self.testString1 = string; // 指向同一块内存空间
self.testString2 = string; // 浅拷贝,拷贝指向对象的指针,指向同一块内存空间
NSLog(@"string = %@ 指针地址 = %p", string, string);
NSLog(@"_testString1 = %@ 指针地址 = %p", self.testString1, self.testString1);
NSLog(@"_testString2 = %@ 指针地址 = %p", self.testString2, self.testString2);
string = [NSString stringWithFormat:@"123456"];
NSLog(@"string = %@ 指针地址 = %p", string, string);
NSLog(@"_testString1 = %@ 指针地址 = %p", self.testString1, self.testString1);
NSLog(@"_testString2 = %@ 指针地址 = %p", self.testString2, self.testString2);
}
打印输出
2020-04-01 19:45:15.593923+0800 CopyDemo[3910:52418] string = MyTest 指针地址 = 0xfb5fa968f64c2397
2020-04-01 19:45:15.594247+0800 CopyDemo[3910:52418] _testString1 = MyTest 指针地址 = 0xfb5fa968f64c2397
2020-04-01 19:45:15.594386+0800 CopyDemo[3910:52418] _testString2 = MyTest 指针地址 = 0xfb5fa968f64c2397
2020-04-01 19:45:15.594507+0800 CopyDemo[3910:52418] string = 123456 指针地址 = 0xfb5b8d0de0389457
2020-04-01 19:45:15.594666+0800 CopyDemo[3910:52418] _testString1 = MyTest 指针地址 = 0xfb5fa968f64c2397
2020-04-01 19:45:15.594816+0800 CopyDemo[3910:52418] _testString2 = MyTest 指针地址 = 0xfb5fa968f64c2397
分析:
我们可以看到,在string赋值成@“123456”之前,string、self.testString1和self.testString2都指向同一块内存空间,内容自然也相同。
(1) 当来源对象也是NSString的时候,strong和copy一样
(2) copy操作如果来源对象是否可变,如果来源对象是不可变对象(NSString),则只拷贝指针,也就是浅拷贝;
(3) copy操作如果来源对象是可变对象(NSMutableString),则拷贝对象内容,也就是深拷贝
string赋值成@“123456”之后,string指向了新的一块内存空间,也就是@"123456"的内存空间;
而self.testString1和self.testString2仍指向之前的一块内存空间,也就是@"MyTest"的内存空间,所以内容还是@“MyTest”
再来一段代码(有陷阱)
// 深复制1
- (IBAction)deepCopy1Action:(id)sender {
NSMutableString *string = [NSMutableString stringWithString:@"MyTest"];
_testString1 = string; // 复制对象指针地址
_testString2 = string; // 复制对象指针地址
NSLog(@"string = %@ 指针地址 = %p", string, string);
NSLog(@"_testString1 = %@ 指针地址 = %p", self.testString1, self.testString1);
NSLog(@"_testString2 = %@ 指针地址 = %p", self.testString2, self.testString2);
[string appendString:@"...."];
NSLog(@"string = %@ 指针地址 = %p", string, string);
NSLog(@"_testString1 = %@ 指针地址 = %p", self.testString1, self.testString1);
NSLog(@"_testString2 = %@ 指针地址 = %p", self.testString2, self.testString2);
}
**
打印输出
**
2020-04-01 19:55:00.595529+0800 CopyDemo[4311:63637] string = MyTest 指针地址 = 0x600000beadc0
2020-04-01 19:55:00.595841+0800 CopyDemo[4311:63637] _testString1 = MyTest 指针地址 = 0x600000beadc0
2020-04-01 19:55:00.595991+0800 CopyDemo[4311:63637] _testString2 = MyTest 指针地址 = 0x600000beadc0
2020-04-01 19:55:00.596119+0800 CopyDemo[4311:63637] string = MyTest.... 指针地址 = 0x600000beadc0
2020-04-01 19:55:00.596228+0800 CopyDemo[4311:63637] _testString1 = MyTest.... 指针地址 = 0x600000beadc0
2020-04-01 19:55:00.596332+0800 CopyDemo[4311:63637] _testString2 = MyTest.... 指针地址 = 0x600000beadc0
这次为什么,内容和指针地址同步了,都一样?
在[string appendString:@"…"];这个语句执行前,string、self.testString1和selfString2都指向同一块内存空间,内容自然也相同;
本质原理:
用@property来声明属性变量时,编译器会自动生成一个以下划线加属性名命名的实例变量
(@synthesize testString1 = _testString1)
(@synthesize testString2 = _testString2)
并且生成对应的getter和setter方法,对于copy修饰的testString2,setter方法中有一条语句:_testString2 = [testString2 copy];
用_testString2 = string; 并没有调用testString2的setter方法
用self.testString2 = string才会调用testString2的setter方法
所以,string、self.testString1和self.testString2和string指向的是同一块内存空间,内容也相同;
[string appendString:@"…"];并没有改变内存空间地址,在字符串尾部添加了@"…"
// 深复制1
- (IBAction)deepCopy1Action:(id)sender {
NSMutableString *string = [NSMutableString stringWithString:@"MyTest"];
self.testString1 = string; // 指向同一块内存空间
self.testString2 = string; // 深拷贝,拷贝对象内容,放入重新开辟的一片内存空间
// 当指向对象也是NSMutableString的时候,strong和copy不一样
NSLog(@"string = %@ 指针地址 = %p", string, string);
NSLog(@"_testString1 = %@ 指针地址 = %p", self.testString1, self.testString1);
NSLog(@"_testString2 = %@ 指针地址 = %p", self.testString2, self.testString2);
[string appendString:@"...."];
NSLog(@"string = %@ 指针地址 = %p", string, string);
NSLog(@"_testString1 = %@ 指针地址 = %p", self.testString1, self.testString1);
NSLog(@"_testString2 = %@ 指针地址 = %p", self.testString2, self.testString2);
}
打印输出:
2020-04-01 20:09:29.139054+0800 CopyDemo[4561:70490] string = MyTest 指针地址 = 0x60000164d770
2020-04-01 20:09:29.139231+0800 CopyDemo[4561:70490] _testString1 = MyTest 指针地址 = 0x60000164d770
2020-04-01 20:09:29.139361+0800 CopyDemo[4561:70490] _testString2 = MyTest 指针地址 = 0xbe43ad479fcbd6af
2020-04-01 20:09:29.139474+0800 CopyDemo[4561:70490] string = MyTest.... 指针地址 = 0x60000164d770
2020-04-01 20:09:29.139591+0800 CopyDemo[4561:70490] _testString1 = MyTest.... 指针地址 = 0x60000164d770
2020-04-01 20:09:29.139704+0800 CopyDemo[4561:70490] _testString2 = MyTest 指针地址 = 0xbe43ad479fcbd6af
string和self.testString1指向同一块内存空间,地址相同,内容相同;
self.testString2重新复制string的内容,重新开辟了一块内存空间,当string的内容发生改变,并不会影响self.testString2的内容;
总结:
(1) 当来源对象也是NSString的时候,strong和copy一样
(2) copy操作如果来源对象是否可变,如果来源对象是不可变对象(NSString),则只拷贝指针,也就是浅拷贝;
(3) copy操作如果来源对象是可变对象(NSMutableString),则拷贝对象内容,也就是深拷贝
**
浅拷贝,也就是浅复制,本质是拷贝对象指针;
深拷贝,也就是深复制,本质是拷贝对象内容;
**