一、为什么@property声明(NString,NSArray,NSDictionary)时需要使用copy,使用strong有什么问题。
- 因为NString,NSArray,NSDictionary都有自己对应的子类:NSMutableString,NSMutableArray,NSMutableDictionary,而父类指针可以指向子类对象,使用copy可以让本对象不受外界(子对象)影响,无论给我传入的是一个可变对象还是一个不可变对象,都能保证自身持有的是一个不可变副本。
- 使用strong时,如果这个属性指向一个可变对象,修改可变对象时,这个属性值也会被修改。
举例说明:
定义两个属性string和array用strong修饰
@interface ViewController ()
@property (strong, nonatomic) NSString *string;
@property (strong, nonatomic) NSArray *array;
@end
@implementation ViewController
- (void)viewDidLoad {
NSMutableString * mString = [NSMutableString stringWithString:@"123"];
self.string = mString;
[mString appendString:@"666"];
NSLog(@"%@",self.string);
NSMutableArray *mArray = [NSMutableArray arrayWithObjects:@1,@2,@3, nil];
self.array = mArray;
[mArray addObject:@6];
NSLog(@"%@",self.array);
}
打印结果:
2017-06-02 17:43:22.440 测试Test[45671:7372719] 123666
2017-06-02 17:43:22.441 测试Test[45671:7372719] (
1,
2,
3,
6
)
这里的属性string和array分别被赋值子类可变对象mString和mArray,再分别修改mString和mArray导致string和array都被修改了。
下面看看使用copy关键字后的结果:(所有代码不变,只是strong改成copy)
打印结果:
2017-06-02 17:48:32.583 测试Test[45795:7380495] 123
2017-06-02 17:48:32.583 测试Test[45795:7380495] (
1,
2,
3
)
属性string和array都没有被修改。所以使用copy能保证属性不被子类对象修改时同时被修改。
二、深拷贝和浅拷贝
- 深拷贝:内容的拷贝
- 浅拷贝:地址的拷贝
1、对非集合对象的copy和mutableCopy
- (void)viewDidLoad {
[super viewDidLoad];
NSString *string = @"123";
NSString *stringCopy = [string copy];
NSLog(@"string的地址:%p,stringCopy的地址:%p",string,stringCopy);
NSMutableString *stringMCopy = [string mutableCopy];
NSLog(@"stringMCopy的地址:%p",stringMCopy);
NSMutableString * mString = [NSMutableString stringWithString:@"123"];
NSString *mStringCopy = [mString copy];
NSLog(@"mString的地址:%p,mStringCopy的地址:%p",mString,mStringCopy);
NSMutableString *mStringMCopy = [mString mutableCopy];
NSLog(@"mStringMCopy的地址:%p",mStringMCopy);
}
打印结果:
2017-06-03 10:22:58.643 测试Test[61063:7767634] string的地址:0x10d696078,stringCopy的地址:0x10d696078
2017-06-03 10:22:58.644 测试Test[61063:7767634] stringMCopy的地址:0x61000006a640
2017-06-03 10:22:58.644 测试Test[61063:7767634] mString的地址:0x61000006d040,mStringCopy的地址:0xa000000003332313
2017-06-03 10:22:58.644 测试Test[61063:7767634] mStringMCopy的地址:0x600000072200
2、对集合对象的copy和mutableCopy
集合对象指的是NSArray,NSDictionary,NSSet等类的对象。
- (void)viewDidLoad {
[super viewDidLoad];
NSArray *array = @[@1, @2, @3, @4];
NSArray *arrayCopy = [array copy];
NSMutableArray *arrayMCopy = [array mutableCopy];
NSLog(@"array的地址:%p,arrayCopy的地址:%p,arrayMCopy的地址:%p",array,arrayCopy,arrayMCopy);
NSMutableArray *mArray = [NSMutableArray arrayWithObjects:@1, @2, @3, @4, nil];
NSArray *mArrayCopy = [mArray copy];
NSMutableArray *mArrayMCopy = [mArray mutableCopy];
NSLog(@"mArray的地址:%p,mArrayCopy的地址:%p,mArrayMCopy的地址:%p",mArray,mArrayCopy,mArrayMCopy);
}
打印结果:
2017-06-03 10:50:14.247 测试Test[61941:7806421] array的地址:0x600000058090,arrayCopy的地址:0x600000058090,arrayMCopy的地址:0x600000057b50
2017-06-03 10:50:14.247 测试Test[61941:7806421] mArray的地址:0x6100000556c0,mArrayCopy的地址:0x610000055780,mArrayMCopy的地址:0x610000055870
可以得出结论集合对象的copy和mutableCopy和非集合对象相同的结果:
- [immutableObject copy]是浅拷贝
- [immutableObject mutableCopy]是深拷贝
- [mutableObject copy]是深拷贝
- [mutableObject mutableCopy]是深拷贝
注意事项:
集合对象的深拷贝只是对象本身,而集合对象的元素还是地址的拷贝,即单层深拷贝:
- (void)viewDidLoad {
[super viewDidLoad];
NSMutableArray *mArray1 = [NSMutableArray arrayWithObjects:@2, @2, @3, @4, nil];
NSArray *array = @[mArray1, @2, @3, @4];
NSArray *arrayCopy = [array copy];
NSMutableArray *arrayMCopy = [array mutableCopy];
NSLog(@"array的地址:%p,arrayCopy的地址:%p,arrayMCopy的地址:%p",array,arrayCopy,arrayMCopy);
NSMutableArray *mArray = [NSMutableArray arrayWithObjects:mArray1, @2, @3, @4, nil];
NSArray *mArrayCopy = [mArray copy];
NSMutableArray *mArrayMCopy = [mArray mutableCopy];
NSLog(@"mArray的地址:%p,mArrayCopy的地址:%p,mArrayMCopy的地址:%p",mArray,mArrayCopy,mArrayMCopy);
NSLog(@"array的第一个元素地址:%p,arrayMCopy的第一个元素地址:%p",array[0],arrayMCopy[0]);
NSLog(@"mArray的第一个元素地址:%p,mArrayMCopy的第一个元素地址:%p",mArray[0],mArrayMCopy[0]);
}
打印结果:
2017-06-03 11:08:52.382 测试Test[62621:7832878] array的地址:0x618000054c70,arrayCopy的地址:0x618000054c70,arrayMCopy的地址:0x618000054cd0
2017-06-03 11:08:52.382 测试Test[62621:7832878] mArray的地址:0x600000056020,mArrayCopy的地址:0x600000055fc0,mArrayMCopy的地址:0x600000056080
2017-06-03 11:08:52.383 测试Test[62621:7832878] array的第一个元素地址:0x6180000548e0,arrayMCopy的第一个元素地址:0x6180000548e0
2017-06-03 11:08:52.383 测试Test[62621:7832878] mArray的第一个元素地址:0x6180000548e0,mArrayMCopy的第一个元素地址:0x6180000548e0
三、block为什么要使用copy
block使用copy是在MRC中延续下来的,在MRC下,方法内部的block如果有被copy属性修饰,且捕获了外部变量,那么他会被copy到堆上;如果被assgin修饰的,且捕获了外部变量,那么他是stack_block,是在栈上的;如果仅仅只是一个copy的属性的block,么有捕获外部变量,它还是是global_block,它是在全局区。
在ARC下编译器会自动对block进行copy,因此我们使用copy或者strong修饰的效果是一样的。但是我们在ARC下继续使用copy可以提醒我们编译器会自动帮我们实现copy的操作。至于block是哪中类型的,他是在内存的哪个区的,和MRC下的是一样的,要看block有木有引用外部变量。