iOS Strong和Copy 理解

问题描述

在定义一个类的property时候,为property选择strong还是copy特别注意和研究明白的,如果property是NSString或者NSArray及其子类的时候,最好选择使用copy属性修饰。为什么呢?这是为了防止赋值给它的是可变的数据,如果可变的数据发生了变化,那么该property也会发生变化。

代码示例 

首先声明了两个NSString属性,如下:

@property(nonatomic, strong) NSString *strongStr;
@property(nonatomic, copy) NSString *copyyStr;

下面我们分别以NSString和NSMutableString举例

NSString场景一

NSString 场景一

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

NSString场景二   

NSString场景二

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

NSMutableString场景一

NSMutableString场景一

结论:不论是用strong还是copy修饰的对象,其指针指向的地址依然还是originStr的地址。
疑问:为什么_copyyStr的值会变成“hello,I changed”呢?不应该是“hello,originStr”吗? 

NSMutableString场景二

NSMutableString场景二

疑问:self.copyyStr值:hello,originStr 终于是我们想要的结果
_copyyStr = originStr;和self.copyyStr = originStr;有什么区别?

原因:
用@property来声明属性变量时,编译器会自动为我们生成一个以下划线加属性名命名的实例变量(@synthesize copyyStr = _copyyStr),并且生成其对应的getter、setter方法。
1.self.copyyStr = originStr赋值时,会调用coppyStr的setter方法
2._copyyStr = originStr 赋值时给_copyyStr实例变量直接赋值,并不会调用copyyStr的setter方法
而在setter方法中有一个非常关键的语句:

_copyyStr = [copyyStr copy];

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

总结
由上面的例子可以得出:

1.当原字符串是NSString时,由于是不可变字符串,所以,不管使用strong还是copy修饰,都是指向原来的对象,copy操作只是做了一次浅拷贝。
2.当源字符串是NSMutableString时,strong只是将源字符串的引用计数加1,而copy则是对原字符串做了次深拷贝,从而生成了一个新的对象,并且copy的对象指向这个新对象。

所以,如果源字符串是NSMutableString的时候,使用strong只会增加引用计数。但是copy会执行一次深拷贝,会造成不必要的内存浪费。而如果原字符串是NSString时,strong和copy效果一样,就不会有这个问题。
但是,我们一般声明NSString时,也不希望它改变,所以一般情况下,建议使用copy,这样可以避免NSMutableString带来的错误。

简要阐述内存相关的关键字?

Strong

指向并持有该对象,引用计数会加1。引用计数为0销毁,可以通过将变量强制赋值 nil 来进行销毁。

Weak

指向但是并不持有该对象,引用计数不会加1。在 Runtime 中对该属性进行了相关操作,无需处理,可以自动销毁

assign

assign主要用于修饰基本数据类型,例如NSInteger,CGFloat,存储在栈中,内存不用程序员管理

copy

copy关键字和 strong类似,copy 多用于修饰有可变类型的不可变对象上 NSString,NSArray,NSDictionary上。

__unsafe_unretain

__unsafe_unretain 类似于 weak ,但是当对象被释放后,指针依然保存着之前的地址,被释放后的地址变为 僵尸对象,访问被释放的地址就会出问题,所以说他是不安全的。

atomic:

这个属性是为了保证在多线程的情况下,编译器会自动生成一些互斥加锁的代码,避免该变量的读写不同步的问题。

nonatomic

如果该对象无需考虑多线程的情况,这个属性会让编译器少生成一些互斥代码,可以提高效率。

__weak 和 __unsafe_unretain 的区别?

__unsafe_unretain 在指向的内存地址销毁后,指针本身并不会自动销毁,这也就造成了野指针,之后容易造成 Crash。__weak 在指向的内存销毁后,可以将指针变量置为 nil,这样更加安全。

__weak 修饰的变量在地址被释放后,为何被置为 nil?

在 Runtime 中专门维护了一个用于存储 weak指针变量的 Hash 表。这个表 key 是 weak指针所指向的内存地址,value 是指向这个内存地址的所有 weak指针,实际上是一个数组。释放时根据对象地址获取所有 weak指针 地址的数组,然后遍历这个数组把其中的数据设为 nil,最后把这个 对象 从weak表 中删除。

使用atomic一定是线程安全的吗?

atomic可以保证setter和getter存取的线程安全并不保证整个对象是线程安全的。
比如,声明一个NSMutableArray的原子属性array,此时self.array和self.array = otherArray都是线程安全的。但是,使用[self.array objectAtIndex:index]就不是线程安全的,需要用锁来保证线程安全性。


 

猜你喜欢

转载自blog.csdn.net/LIN1986LIN/article/details/86289153
今日推荐