iOS strong和copy的区别

栏目: IOS · 发布时间: 6年前

内容简介:在iOS开发中,几乎每天都会遇到在NSString属性声明有两个关键字可以选择:

在iOS开发中,几乎每天都会遇到 NSString 属性的声明,

ARC内存管理机制 下,

NSString属性声明有两个关键字可以选择: strongcopy

那么问题来了,什么时候用 strong ,什么时候用 copy

下面我写一个小demo,希望大家能看懂,也还请路过的大神指教!

我在.h文件中声明了两个 NSString 属性,如下:

@property(nonatomic, strong) NSString *strongStr;

@property(nonatomic, copy) NSString *copyyStr;
// 注:不能以alloc,new,copy,mutableCopy 作为开头命名,比如:copyStr
复制代码

第一种场景:用NSString直接赋值(错误案例)

// 第一种场景:用NSString直接赋值
NSString *originStr1 = [NSString stringWithFormat:@"hello,everyone"];

_strongStr = originStr1;
_copyyStr = originStr1;
    
NSLog(@"第一种场景:用NSString直接赋值");
NSLog(@"               对象地址         对象指针地址        对象的值   ");
NSLog(@"originStr: %p , %p , %@", originStr1, &originStr1, originStr1);
NSLog(@"strongStr: %p , %p , %@", _strongStr, &_strongStr, _strongStr);
NSLog(@" copyyStr: %p , %p , %@", _copyyStr, &_copyyStr, _copyyStr);
复制代码

然后我们运行一下,打印结果如下图:

iOS strong和copy的区别

结论:这种情况下,不管是用strong还是copy修饰的对象,其指向的地址都是originStr的地址。

第二种场景:用NSMutableString直接赋值(错误案例)

// 第二种场景:用NSMutableString直接赋值
NSMutableString *originStr2 = [NSMutableString stringWithFormat:@"hello,everyone"];

_strongStr = originStr2;
_copyyStr = originStr2;

[originStr2 setString:@"hello,QiShare"];
    
NSLog(@"第二种场景:用NSMutableString直接赋值");
NSLog(@"               对象地址         对象指针地址        对象的值   ");
NSLog(@"originStr: %p , %p , %@", originStr2, &originStr2, originStr2);
NSLog(@"strongStr: %p , %p , %@", _strongStr, &_strongStr, _strongStr);
NSLog(@" copyyStr: %p , %p , %@", _copyyStr, &_copyyStr, _copyyStr);
复制代码

然后我们运行一下,打印结果如下图:

iOS strong和copy的区别

看到这里,同学们可能会有疑问,为什么不论是用 strong 还是 copy 修饰的对象,其指针指向的地址 依然还是originStr的地址 ?为什么_copyyStr的值会变成“hello,QiShare”呢?不应该是“hello,everyone”吗?

咱们先不解释,卖个关子,我们接着往下看。

第三种场景:用NSMutableString点语法赋值

// 第三种场景:用NSMutableString点语法赋值
NSMutableString *originStr3 = [NSMutableString stringWithFormat:@"hello,everyone"];
    
self.strongStr = originStr3;
self.copyyStr = originStr3;
    
[originStr3 setString:@"hello,QiShare"];
    
NSLog(@"第三种场景:用NSMutableString点语法赋值");
NSLog(@"               对象地址         对象指针地址        对象的值   ");
NSLog(@"originStr: %p , %p , %@", originStr3, &originStr3, originStr3);
NSLog(@"strongStr: %p , %p , %@", _strongStr, &_strongStr, _strongStr);
NSLog(@" copyyStr: %p , %p , %@", _copyyStr, &_copyyStr, _copyyStr);
复制代码

然后我们运行一下,打印结果如下图:

iOS strong和copy的区别

OK,这回我们终于看到我们希望看到的结果了,

_copyyStr依然是“hello,everyone”,没有变成“hello,QiShare”,

_copyyStr指针指向的地址不再是_originStr的地址。

细心的同学会发现,第三种在赋值的时候用了 点语法 ,而不是直接赋值。

除了将 _strongStr = originStr2; 改为 self.strongStr = originStr3;

_copyyStr = originStr2;改为 self.copyyStr = originStr3;

其余完全一样。

也就是说,我们将_copyyStr = originStr2;改为 self.copyyStr = originStr3;才导致了_copyyStr的值在第三种情况下依然没有改变,这是为什么呢?

当我们用@property来声明属性变量时,编译器会自动为我们生成一个以下划线加属性名命名的实例变量(@synthesize copyyStr = _copyyStr),并且生成其对应的getter、setter方法。

当我们用self.copyyStr = originStr赋值时,会调用coppyStr的setter方法,而_copyyStr = originStr 赋值时给_copyyStr实例变量直接赋值,并不会调用copyyStr的 setter方法 ,而在 setter方法 中有一个非常 关键 的语句:

_copyyStr = [copyyStr copy];
复制代码

结论:第三种场景中用self.copyyStr = originStr 赋值时,调用copyyStr的setter方法,setter方法对传入的copyyStr做了次 深拷贝 生成了一个新的对象赋值给_copyyStr,所以_copyyStr指向的地址和对象值都不再和originStr相同。

第四种场景:用NSString点语法赋值

// 第四种场景:用NSString点语法赋值
NSString *originStr4 = [NSString stringWithFormat:@"hello,everyone"];

self.strongStr = originStr4;
self.copyyStr = originStr4;
    
NSLog(@"第三种场景:用NSMutableString点语法赋值");
NSLog(@"               对象地址         对象指针地址        对象的值   ");
NSLog(@"originStr: %p , %p , %@", originStr4, &originStr4, originStr4);
NSLog(@"strongStr: %p , %p , %@", _strongStr, &_strongStr, _strongStr);
NSLog(@" copyyStr: %p , %p , %@", _copyyStr, &_copyyStr, _copyyStr);
复制代码

这里我们将_copyyStr = originStr;改成了self.copyyStr = originStr;

这时候打印结果会是什么样呢?

iOS strong和copy的区别

看了打印结果,可能有的同学会产生疑问,为什么用了self.copyyStr = originStr进行赋值,调用了setter方法,调用了_copyyStr = [copyyStr copy]之后,_copyyName指向的地址和originStr指向的地址还是相同的呢?

原因:这里的copy是浅拷贝,并没有生成新的对象

总结:

由上面的例子可以得出:

  • 当原字符串是 NSString 时,由于是不可变字符串,所以,不管使用 strong 还是 copy 修饰,都是指向原来的对象, copy 操作只是做了一次浅拷贝。
  • 而当源字符串是 NSMutableString 时, strong 只是将源字符串的 引用计数加1 ,而 copy 则是对原字符串做了次 深拷贝 ,从而生成了一个新的对象,并且copy的对象指向这个新对象。另外需要注意的是,这个copy属性对象的类型始终是 NSString ,而不是NSMutableString,如果想让拷贝过来的对象是可变的,就要使用 mutableCopy
    所以,如果源字符串是 NSMutableString 的时候,使用strong只会增加引用计数。
    但是copy会执行一次深拷贝,会造成不必要的 内存浪费 。而如果原字符串是NSString时,strong和copy效果一样,就不会有这个问题。

但是,我们一般声明NSString时,也不希望它改变,所以一般情况下,建议使用 copy ,这样可以避免NSMutableString带来的错误。

顺便路过提一下assign与weak

我们都知道, assign 用来修饰基本数据类型, weak 用来修饰OC对象。

其实照理, assign 也能修饰 OC对象 ,但是 assign 修饰的对象在该对象释放后,其指针依然存在,不会被置为nil——这就造成了一个很严重的问题:出现了 野指针 。当访问这个野指针时,指向了原地址,而原地址有两种情况:

  • 第一种情况:原地址没有改变,代码运行通过,但很有可能有逻辑bug。
  • 第二种情况:原地址已经改变,结果不可预测,多数崩溃,也有可能出现其他莫名错误。

但是用 weak 来修饰的话,对象释放的时候会把指针置为 nil ,从而 避免了野指针 的出现。

那又有个疑问出现了,凭什么基本数据类型就可以使用 assign 。这就要扯到 的问题了,基本数据类型会被分配到栈空间,而栈空间是由 系统自动管理分配和释放 的,就不会造成野指针的问题。

ps:本文demo链接: github.com/QiShare/QiS…

转载自:https://www.jianshu.com/p/62913d6cbc40


以上所述就是小编给大家介绍的《iOS strong和copy的区别》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Learning PHP 5

Learning PHP 5

David Sklar / O'Reilly / July, 2004 / $29.95

Learning PHP 5 is the ideal tutorial for graphic designers, bloggers, and other web crafters who want a thorough but non-intimidating way to understand the code that makes web sites dynamic. The book ......一起来看看 《Learning PHP 5》 这本书的介绍吧!

RGB转16进制工具
RGB转16进制工具

RGB HEX 互转工具

Markdown 在线编辑器
Markdown 在线编辑器

Markdown 在线编辑器

RGB CMYK 转换工具
RGB CMYK 转换工具

RGB CMYK 互转工具