初探 属性的copy、strong

先看一段代码,从代码中发现问题,再解决问题。

首先是一个Apple类,该类有两个属性,birthDate和productor。

#import <Foundation/Foundation.h>

@interface Apple : NSObject

@property (nonatomic, copy) NSString *birthDate;
@property (nonatomic, copy) NSString *productor;
@end


再是一个Fruit类,这个类有五个集合属性。

@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

-------------------------------------

问题6.h文件有个 strong-NSArray的fruit属性,.m文件的类扩展中有个同名的copy-NSMutable *_fruit 成员变量
从上面的知识点可以总结出来,不可变类型使用strong限定就可以,可变类型要使用copy限定,这样做能在给类属性 赋值的时候 保护类的属性不被篡改。
但是在取值的时候,单纯依靠属性的限定还是不够的。
看段代码:

<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>

打印结果:


-------------------------------------
改变之后:
fruits1.birthDate=xiaoXXX0
fruits2.birthDate=xiaoXXX0
fruits3.birthDate=xiaoXXX0
fruits4.birthDate=xiaoXXX0
fruit.birthDate  =xiaoXXX0
-------------------------------------

也就是说,不管是怎么限定NSArray和NSMutableArray的还是.h文件限定一个strong-NSArray的fruit,.m的类扩展中声明一个 copy-NSMutable *_fruit 都没幸免取值后类的属性值被篡改的厄运。

首先还是问题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类型的。


对了,忘记看打印结果了:

-------------------------------------
改变内容之后:
fruits1.count = 0
fruits2.count = 1
fruits3.count = 1
fruits4.count = 1
fruit.count   = 1


strong限定的mutableArray fruits1 就被篡改了,其他没有。得益于NSArray,NSArray本身就是不可变的。

************************************************************
总结:看完上面的知识,好像得出来不管可变不可变对象都用copy限定行了。但是实际上,我们都不这么做,我们的一般做法是可变对象 strong,不可变copy。这样不是会导致篡改问题吗?是不是又讲错了。我觉得没有讲错,因为代码在那里,就是这样的。我又推测,之所以标准写法将可变对象用strong限定,不可变用copy限定,是因为别的原因的,至于什么原因,我还不知道,这就是我以后需要学习的。也不排除这篇博客从头开始就是错的。如果按照标准写法,strong限定可变类型,那怎么保证类的成员变量不被篡改呀?头文件中声明一个copy的属性,类扩展中声明一个同名的strong可变成员变量就能保证



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值