OC中与copy有关的那些事 一 (copy与声明NSString属性 : strong/copy 的关系)

来源:互联网 发布:招聘网络平面设计师 编辑:程序博客网 时间:2024/05/18 13:04

前言:

最近有时间,想要把oc的基础知识重新梳理巩固一遍,在学习当中又有了新的理解,自己总结之后记录一下,以免过后又糊涂。。。首先就从copy相关的内容下手吧!

第1节、深拷贝与浅拷贝

谈到copy,肯定要聊到OC的深拷贝与浅拷贝(又称为内存拷贝与指针拷贝)。这也是面试时面试官经常会问起的题目。关于深拷贝与浅拷贝,网上已经有很多优秀的文章探讨过了,但因为这篇的内容跟深、浅拷贝有一定关系,还是说明一下。

首先明确一点: copy出的对象为imutable(不可变)类型,mutableCopy出的对象为mutable(可变)类型

对于NSString:copy/mutableCopy

    NSString *origanlStr = [NSString stringWithFormat:@"abc"];    //没产生新内存空间    NSString *copyStr = [origanlStr copy];    //产生了新内存空间    NSMutableString *mutableStr = [origanlStr mutableCopy];    NSLog(@"---origanlStr is %@, %p", origanlStr, origanlStr);    NSLog(@"---copyStr is %@, %p", copyStr, copyStr);    NSLog(@"---mutableStr is %@, %p", mutableStr, mutableStr);

打印结果为:

2017-02-21 16:15:39.606 TestProiect[56256:3824325] ---origanlStr is abc, 0xa0000000063626132017-02-21 16:15:39.607 TestProiect[56256:3824325] ---copyStr is abc, 0xa0000000063626132017-02-21 16:15:39.607 TestProiect[56256:3824325] ---mutableStr is abc, 0x60000006a140

可以看到:对NSString对象来说,copy不会产生新对象,mutableCopy会产生新的NSMutableString对象。

对于NSMutableString:copy/mutableCopy

NSMutableString *origanlStr = [NSMutableString stringWithFormat:@"abc"];    //产生了新内存空间    NSString *copyStr = [origanlStr copy];    //产生了新内存空间    NSMutableString *mutableStr = [origanlStr mutableCopy];    NSLog(@"---origanlStr is %@, %p", origanlStr, origanlStr);    NSLog(@"---copyStr is %@, %p", copyStr, copyStr);    NSLog(@"---mutableStr is %@, %p", mutableStr, mutableStr);

打印结果为:

2017-02-21 16:26:28.484 TestProiect[56276:3831735] ---origanlStr is abc, 0x6100002610802017-02-21 16:26:28.484 TestProiect[56276:3831735] ---copyStr is abc, 0xa0000000063626132017-02-21 16:26:28.484 TestProiect[56276:3831735] ---mutableStr is abc, 0x6100002610c0

可以看到:对NSMutableString对象来说,copy和mutableCopy会产生新的对象。

这样我们可以得到结论如下图:

这里写图片描述

在上面的这幅图中,我们把结论扩展到了NSArray和其他的NS对象。但注意,对于NSArray、NSDictionary这类的容器对象(也就是可以容纳其他元素的对象)来做copy或mutableCopy时,对NSArray、NSDictionary类型的对象本身来说是满足上面表里的原则的,也就是与NSString类似。但对于这些容器内部的元素来说,只会进行指针拷贝,并不会为其开辟出新的内存空间。
对于其他的NS对象,则必须满足NSCopying和NSMutableCopying协议才能执行copy或mutableCopy消息,否则会导致程序崩溃。
对于容器类的深浅拷贝,由于需要考虑较多,本篇先不做过多说明,之后会开一篇新的博客解释。

第2节、用strong还是用copy?

了解完了深浅拷贝的基本使用,下面进入正题:在声明NSString类型的属性时大家都知道要用copy来修饰。但是具体是为什么呢?下面就详细的分析一下。

①:我们首先用strong和copy分别修饰NSString类型的属性:

@property (nonatomic, copy) NSString *myCopyStr;@property (nonatomic, strong) NSString *myStrongStr;
NSString *origanlStr = [NSString stringWithFormat:@"abc"];    self.myCopyStr = origanlStr;    self.myStrongStr = origanlStr;    origanlStr = @"456";    NSLog(@"---origanlStr is %@, %p", origanlStr, origanlStr);    NSLog(@"---myCopyStr is %@, %p", self.myCopyStr, self.myCopyStr);    NSLog(@"---myStrongStr is %@, %p", self.myStrongStr, self.myStrongStr);

打印出的结果是这样的:

2017-02-21 17:04:03.336 TestProiect[56347:3867212] ---origanlStr is 456, 0x10ecbde902017-02-21 17:04:03.336 TestProiect[56347:3867212] ---myCopyStr is abc, 0xa0000000063626132017-02-21 17:04:03.337 TestProiect[56347:3867212] ---myStrongStr is abc, 0xa000000006362613

看起来用strong和copy修饰的NSString类型属性表现完全一样嘛。。。那为什么还要区分用strong和copy修饰呢?

②:那我们再看下面的代码:

 NSMutableString *origanlStr = [NSMutableString stringWithFormat:@"abc"];    self.myCopyStr = origanlStr;    self.myStrongStr = origanlStr;    [origanlStr appendString:@"456"];    NSLog(@"---origanlStr is %@, %p", origanlStr, origanlStr);    NSLog(@"---myCopyStr is %@, %p", self.myCopyStr, self.myCopyStr);    NSLog(@"---myStrongStr is %@, %p", self.myStrongStr, self.myStrongStr);

在这段代码里,我们把元字符串origanlStr变为了NSMutableString类型,并且在后面改变了origanlStr的值(在原先的”abc”后面拼接了”456”)。
这样打印出来的结果为:

2017-02-21 17:12:13.722 TestProiect[56370:3876839] ---origanlStr is abc456, 0x6000000741002017-02-21 17:12:13.723 TestProiect[56370:3876839] ---myCopyStr is abc, 0xa0000000063626132017-02-21 17:12:13.723 TestProiect[56370:3876839] ---myStrongStr is abc456, 0x600000074100

这样就看出区别了吧,如果是用copy类型声明的NSString类型属性,如果元字符串为可变类型,在元字符串值改变之后,因为其内存地址与元字符串内存地址不一致,其属性值不会随之改变。
而如果是用strong类型声明的NSString类型属性,如果元字符串为可变类型,在元字符串值改变之后,因为其内存地址与元字符串内存地址一样,其属性值也会随之改变。

为什么元字符串是可变或者不可变,会导致这样不同的结果呢?

这是因为如果元字符串为不可变类型(NSString)时:
这些不可变对象是分配在静态存储区的,整个进程公用一份对象,所以无论是strong或copy声明属性,指向的存储空间都是同一个,所以没有区别。又因为元字符串为不可变类型,所以它的值不会变,因此无论是strong或copy声明属性,它们的值也不会变。由此我们得出结论:如果元字符串为不可变类型时,声明属性为strong或copy都一样。

如果元字符串为可变类型(NSMutableString)时:
对于用strong声明的属性,将元字符串赋值给它时,只是将其引用计数器+1,属性指向的内存与元字符串指针指向的内存都为同一块。所以当元字符串改变内存里的值时(拼接了”456”),该属性的值也会随之改变。
对于用copy声明的属性,将元字符串赋值给它时,实际上是将元字符串进行copy。由第一节的结论可知,当元字符串为可变类型时,对其copy是进行了深拷贝。为这个属性开辟了一个新的内存空间,将元字符串的值copy过去。所以这个属性的值不会随元字符串的值改变而改变。
由此我们得出结论:如果元字符串为可变类型时,声明属性为strong的值会随着元字符串改变;声明属性为copy的值不会随着元字符串改变。

一般我们声明NSString类型属性时,都不会希望属性值随着元字符串值改变。所以通常情况下就用copy声明啦。。。


注意:

在上面的分析中,可能会同学有如下疑问:

NSString *origanlStr = [NSString stringWithFormat:@"abc"];    self.myCopyStr = origanlStr;    self.myStrongStr = origanlStr;    origanlStr = @"456";    NSLog(@"---origanlStr is %@, %p", origanlStr, origanlStr);    NSLog(@"---myCopyStr is %@, %p", self.myCopyStr, self.myCopyStr);    NSLog(@"---myStrongStr is %@, %p", self.myStrongStr, self.myStrongStr);

当元字符串为不可变类型(NSString)时,无论是strong或copy声明属性,指向的内存空间都是同一个。当origanlStr变为”456”时,为什么strong或copy属性的值没有随着变化呢?
这是因为当origanlStr变为”456”时,其实是将origanlStr由指向原先”abc”的内存空间重新指向了新的”456”内存空间。但是原先 self.myCopyStr和 self.myStrongStr指向的”abc”的内存空间并没有变化。
举个栗子:两把钥匙A(origanlStr)、B(self.myCopyStr),B复制A后(self.myCopyStr = origanlStr),AB钥匙都可以开(指向)同一把锁(”abc”),后来A重新被磨了(赋值)新的一把钥匙,能开别的锁了(origanlStr = @”456”),但并不能改变B(self.myCopyStr的值仍然为”abc”)。

第3节、copy与属性的关系

上面说了这么多,我们快要接近属性声明copy/strong的本质了。

实际上,当我们声明一个属性为copy时实际上是这样的:

@property (nonatomic, copy) NSString *myCopyStr;
-(void)setMyCopyStr:(NSString *)myCopyStr {    _myCopyStr = [myCopyStr copy];}

是的,在我们声明为copy之后,系统会自动在set方法中执行这个属性的copy方法。这样就实现了这个属性的copy特性,就可以保证它的值不会随着元字符串值的改变而改变啦!

相对的,当我们声明一个属性为strong时实际上是这样的:

@property (nonatomic, strong) NSString *myStrongStr;
-(void)setMyStrongStr:(NSString *)myStrongStr {    _myStrongStr = myStrongStr;}

可以看到,在我们声明为strong之后,系统只是单纯的引用一下,让其引用计数器+1,所以它的值会随着元字符串值的改变而改变。

假设属性改为NSMutableString,会发生什么事?

@property (nonatomic, copy) NSMutableString *myMutableCopyStr;

这样的话它的set方法还是这样:

-(void)setMyMutableCopyStr:(NSMutableString *)myMutableCopyStr {    _myMutableCopyStr = [myMutableCopyStr copy];}

copy出来的仍然是不可变字符!如果有人用NSMutableString的方法,就会崩溃:

 NSMutableString *origanlStr = [NSMutableString stringWithFormat:@"abc"];    self.myMutableCopyStr = origanlStr;    [self.myMutableCopyStr appendString:@"haha"];

这里写图片描述

参考来源:
iOS 浅谈:深.浅拷贝与copy.strong
Copying Collections

0 0
原创粉丝点击