先看一段代码,从代码中发现问题,再解决问题。
首先是一个Apple类,该类有两个属性,birthDate和productor。
#import <Foundation/Foundation.h>
@interface Apple : NSObject
@property (nonatomic, copy) NSString *birthDate;
@property (nonatomic, copy) NSString *productor;
@end
@interface Fruit : NSObject
@property (nonatomic, strong) NSMutableArray *fruits1;
@property (nonatomic, copy) NSMutableArray *fruits2;
@property (nonatomic, strong) NSArray *fruits3;
@property (nonatomic, copy) NSArray *fruits4;
@property (nonatomic, strong) NSArray *fruit;
-(void)showP;
@end
#import "Fruit.h"
#import "Apple.h"
@interface Fruit (){
NSMutableArray *_fruit;
}
@end
@implementation Fruit
-(void)setFruit:(NSArray *)fruit{
_fruit = [fruit mutableCopy];
}
-(NSArray *)fruit{
return [_fruit copy];
}
-(void)showP{
NSLog(@"strong-mutable : %p",_fruits1);
NSLog(@"copy-mutable : %p",_fruits2);
NSLog(@"strong-array : %p",_fruits3);
NSLog(@"copy-array : %p",_fruits4);
}
@end
最后是main,我们要在main中测试,Fruit中的集合属性使用copy、strong修饰的区别。
int main(int argc, const char * argv[]) {
@autoreleasepool {
// insert code here...
Apple *apple = [[Apple alloc]init];
[apple setBirthDate:@"2016-03-07"];
[apple setProductor:@"Shandong"];
NSMutableArray *mutableArray = [NSMutableArray arrayWithObjects:apple, nil];
NSArray *array = [NSArray arrayWithObjects:apple, nil];
Fruit *fruit = [[Fruit alloc]init];
[fruit setFruits1:mutableArray];
[fruit setFruits2:mutableArray];
[fruit setFruits3:array];
[fruit setFruits4:array];
[fruit setFruit:array];
NSLog(@"-------------------------------------");
NSLog(@"用于赋值的数组mutable: %p",mutableArray);
NSLog(@"strong-mutable : %p",[fruit fruits1]);
NSLog(@"copy - mutable : %p",[fruit fruits2]);
NSLog(@"-------------------------------------");
NSLog(@"用于赋值的数组array: %p",array);
NSLog(@"strong-array : %p",[fruit fruits3]);
NSLog(@"copy - array : %p",[fruit fruits4]);
NSLog(@"-------------------------------------");
NSLog(@"改变赋值数组的内容后:");
Apple *apple2 = [[Apple alloc]init];
[apple2 setBirthDate:@"add"];
[apple2 setProductor:@"add"];
array = nil;
[mutableArray addObject:apple2];
NSLog(@"用于赋值的数组mutable: %p",mutableArray);
NSLog(@"strong-mutable-count: %ld",[[fruit fruits1]count]);
NSLog(@"copy - mutable-count: %ld",[[fruit fruits2]count]);
NSLog(@"用于赋值的数组array : %p",array);
NSLog(@"strong-array-count : %ld",[[fruit fruits3]count]);
NSLog(@"copy - array-count : %ld",[[fruit fruits4]count]);
NSLog(@"-------------------------------------");
<span style="white-space:pre"> </span>NSLog(@"改变赋值数组的内容的内容后:");
[apple setBirthDate:@"1111-11-11"];
[apple setProductor:@"SSSSSSS"];
NSLog(@"strong-mutable = %@",[[[fruit fruits1]firstObject]birthDate]);
NSLog(@"strong-mutable = %@",[[[fruit fruits1]firstObject]productor]);
NSLog(@"copy - mutable = %@",[[[fruit fruits2]firstObject]birthDate]);
NSLog(@"copy - mutable = %@",[[[fruit fruits2]firstObject]productor]);
NSLog(@"strong-array = %@",[[[fruit fruits3]firstObject]birthDate]);
NSLog(@"strong-array = %@",[[[fruit fruits3]firstObject]productor]);
NSLog(@"copy - array = %@",[[[fruit fruits4]firstObject]birthDate]);
NSLog(@"copy - array = %@",[[[fruit fruits4]firstObject]productor]);
NSLog(@"-------------------------------------");
NSLog(@"内部实例变量地址:");
[fruit showP];
NSLog(@"-------------------------------------");
}
return 0;
}
main打印结果:
-------------------------------------
用于赋值的数组mutable: 0x10010baa0
strong-mutable : 0x10010baa0
copy - mutable : 0x1001035b0
-------------------------------------
用于赋值的数组array: 0x100109fb0
strong-array : 0x100109fb0
copy - array : 0x100109fb0
-------------------------------------
改变赋值数组的内容后:
用于赋值的数组mutable: 0x10010baa0
strong-mutable-count: 2
copy - mutable-count: 1
用于赋值的数组array : 0x0
strong-array-count : 1
copy - array-count : 1
-------------------------------------
改变赋值数组的内容的内容后:
strong-mutable = 1111-11-11
strong-mutable = SSSSSSS
copy - mutable = 1111-11-11
copy - mutable = SSSSSSS
strong-array = 1111-11-11
strong-array = SSSSSSS
copy - array = 1111-11-11
copy - array = SSSSSSS
-------------------------------------
内部实例变量地址:
strong-mutable : 0x10010baa0
copy-mutable : 0x1001035b0
strong-array : 0x100109fb0
copy-array : 0x100109fb0
-------------------------------------
问题1.深拷贝,浅拷贝
简单地说,深拷贝就是得到了一个新的地址,在这个地址中存放着与被拷贝对象一样的内容;浅拷贝就是拷贝指针了。这样说肯定是不严谨,但绝大多数情况是这个样子的。
问题2.为什么需要使用copy和strong等限定属性
首先,要知道属性其实是类提供给外部访问其成员变量的一种方式。我们在给类的成员变量赋值的时候,尤其是成员变量是集合的时候,是应该对形参深拷贝还是浅拷贝呢?这就需要根据copy和strong来确定了。如果是strong限定的,那就先对成员变量release一次,再将成员变量指向形参的地址(retain),浅拷贝。如果是copy,先对成员变量release一次,然后拷贝形参的内容到一个新地址,成员变量指向这个新地址,recount = 1。
弄明白问题1和问题2,再来验证下是否真的是这样子的。main打印结果:
-------------------------------------
用于赋值的数组mutable: 0x10010baa0
strong-mutable : 0x10010baa0
copy - mutable : 0x1001035b0
-------------------------------------
用于赋值的数组array: 0x100109fb0
strong-array : 0x100109fb0
copy - array : 0x100109fb0
-------------------------------------
首先是一个NSMutableArray类型的变量赋值给Fruit类中的fruits1和fruits2属性。fruits1 strong-mutableArray,fruits2 copy-mutableArray。可以看出,strong限定的fruits1地址管然跟变量地址相同,copy限定的fruits2 地址果然是个新的。再是一个NSArray类型的变量赋值给Fruit类中的 strong-NSArray 的fruits3 和 copy-NSArray的fruits4。strong 限定的fruits3果然跟变量array的地址一样。But,copy限定的fruits4地址怎么还跟array地址一样呢???
问题3.copy-NSArray限定的fruits4 没有指向新地址
刚看到这个结果我也懵了,怀疑是不是上面的理论弄错了。后来查资料,我推测是这个样子的,我推测,是推测哈。
为什么要有深浅拷贝呢,我觉得是,有时候拷贝得到的对象需要不受被拷贝对象的影响,有时候无所谓。
好了,既然被拷贝对象是一个NSArray类型的,即不可变类型的。它都不可变了,还怕什么影响呀;都不怕影响了,干嘛还要再开辟一块内存,就用形参的内存行了呗。所以fruits4就没有指向新的地址了。
再看一段打印结果:
-------------------------------------
改变赋值数组的内容后:
用于赋值的数组mutable: 0x10010baa0
strong-mutable-count: 2
copy - mutable-count: 1
用于赋值的数组array : 0x0
strong-array-count : 1
copy - array-count : 1
-------------------------------------
fruits1 果然变了,它是浅拷贝嘛,指示拷贝了指针,只要有变量操作了那块地址里的内容,所有指向该地址的指针的内容就会变化。fruits2,是深拷贝,新地址,不受被拷贝对象的影像。
NSArray *array 怎么让它=nil了?因为它不可变嘛,不能给他增删。不可变的集合fruits3和fruits4内容都没变。
问题4.不可变怎么"变了"
看打印:
-------------------------------------
改变赋值数组的内容的内容后:
strong-mutable = 1111-11-11
strong-mutable = SSSSSSS
copy - mutable = 1111-11-11
copy - mutable = SSSSSSS
strong-array = 1111-11-11
strong-array = SSSSSSS
copy - array = 1111-11-11
copy - array = SSSSSSS
-------------------------------------
改变apple的内容后,所有的fruits里的apple都变了!!!首先要知道,集合里保存的是指向其他对象的指针。所以Fruit中的所有集合fruits中保存的是指向apple的指针。NSMutable - copy 限定的 fruits2 它是复制了形参mutableArray的内容,存在一个新的地址了,但是,mutableArray的内容是什么呢?是apple的指针,不是它的内容birthDate = @'2016-03-07'。所以Fruit类中所有的fruits内容都变了。
问题5.成员变量
在类内部,它操作的是成员变量,属性只是提供给外部访问的。如果定义了属性,OC会根据属性的限定自动隐含地增加同名成员变量和setter、getter方法。如果非要说,属性跟同名成员变量是都始终指向同一块内存地址,是的,就是这样的。在Fruit类中有个showP方法,打印成员变量的地址。结果如下:
-------------------------------------
内部成员变量地址:
strong-mutable : 0x10010baa0
copy-mutable : 0x1001035b0
strong-array : 0x100109fb0
copy-array : 0x100109fb0
-------------------------------------
<span style="background-color: rgb(255, 255, 255);"><span> </span>NSLog(@"-------------------------------------");
NSArray *tempArray = [NSArray arrayWithObject:apple];
[fruit setFruit:tempArray];
NSLog(@"改变之后:");
NSMutableArray *receiveMutableArray1 = [fruit fruits1];
NSMutableArray *receiveMutableArray2 = [fruit fruits2];
NSArray *receiveArray3 = [fruit fruits3];
NSArray *receiveArray4 = [fruit fruits4];
NSArray *receiveArray = [fruit fruit];
[[receiveMutableArray1 firstObject]setBirthDate:@"xiaoXXX1"];
[[receiveMutableArray2 firstObject]setBirthDate:@"xiaoXXX2"];
[[receiveArray3 firstObject]setBirthDate:@"xiaoXXX3"];
[[receiveArray4 firstObject]setBirthDate:@"xiaoXXX4"];
[[receiveArray firstObject]setBirthDate:@"xiaoXXX0"];
NSLog(@"fruits1.birthDate=%@",[[[fruit fruits1]firstObject]birthDate]);
NSLog(@"fruits2.birthDate=%@",[[[fruit fruits2]firstObject]birthDate]);
NSLog(@"fruits3.birthDate=%@",[[[fruit fruits3]firstObject]birthDate]);
NSLog(@"fruits4.birthDate=%@",[[[fruit fruits4]firstObject]birthDate]);
NSLog(@"fruit.birthDate =%@",[[[fruit fruit]firstObject]birthDate]);
NSLog(@"-------------------------------------");</span>
打印结果:
首先还是问题4的那个说法,我们改的是集合里指针指向的地址里的内容,即改变了内容的内容。管不了那么多,只能管到集合本身能不能变。再看一段代码:
<span style="background-color: rgb(255, 255, 255);"><span> </span>NSArray *tempArray = [NSArray arrayWithObject:apple];
[fruit setFruit:tempArray];
NSMutableArray *receiveMutableArray1 = [fruit fruits1];
NSMutableArray *receiveMutableArray2 = [fruit fruits2];
NSArray *receiveArray3 = [fruit fruits3];
NSArray *receiveArray4 = [fruit fruits4];
NSArray *receiveArray = [fruit fruit];
NSLog(@"-------------------------------------");
NSLog(@"改变内容之后:");
[receiveMutableArray1 removeAllObjects];
//[receiveMutableArray2 addObject:@"lisi"];
NSLog(@"fruits1.count = %ld",[[fruit fruits1]count]);
NSLog(@"fruits2.count = %ld",[[fruit fruits2]count]);
NSLog(@"fruits3.count = %ld",[[fruit fruits3]count]);
NSLog(@"fruits4.count = %ld",[[fruit fruits4]count]);
NSLog(@"fruit.count = %ld",[[fruit fruit]count]);</span>
receiveMutableArray2 addObject:为什么要注释掉?
因为fruits2 返回的是一个NSArray类型,NSArray是不可变的,如果不注释掉,程序编译不通过。receiveMutableArray2 是NSArray类型,是因为fruits2 是由copy限定的。所以在默认的setter方法中,是这个样子的:_fruits2 = [fruits2 copy] ,得到一个NSArray类似的拷贝。即 NSMutableArray *fruits2 实际上是NSArray类型的。