Swift内存管理、weak和unowned以及两者区别(如何使用Swift 中的weak与unowned?)
2016-10-18 09:54
309 查看
Swift 是自动管理内存的,这也就是说,我们不再需要操心内存的申请和分配。
当我们通过初始化创建一个对象时,Swift 会替我们管理和分配内存。而释放的原则遵循了自动引用计数 (ARC) 的规则:当一个对象没有引用的时候,其内存将会被自动回收。这套机制从很大
程度上简化了我们的编码,我们只需要保证在合适的时候将引用置空 (比如超过作用域,或者手动设为
但是,所有的自动引用计数机制都有一个从理论上无法绕过的限制,那就是循环引用 (retain cycle) 的情况。
在
因为即使
在
用通俗的话说,就是
关于两者使用的选择,Apple 给我们的建议是如果能够确定在访问时不会已被释放的话,尽量使用
日常工作中一般使用弱引用的最常见的场景有两个:
设置
在
其中,最常见的delegate使用时,可参考我这篇文章:
当我们通过初始化创建一个对象时,Swift 会替我们管理和分配内存。而释放的原则遵循了自动引用计数 (ARC) 的规则:当一个对象没有引用的时候,其内存将会被自动回收。这套机制从很大
程度上简化了我们的编码,我们只需要保证在合适的时候将引用置空 (比如超过作用域,或者手动设为
nil等),就可以确保内存使用不出现问题。
但是,所有的自动引用计数机制都有一个从理论上无法绕过的限制,那就是循环引用 (retain cycle) 的情况。
什么是循环引用
假设我们有两个类A和
B, 它们之中分别有一个存储属性持有对方:
class A { let b: B init() { b = B() b.a = self } deinit { println("A deinit") } } class B { var a: A? = nil deinit { println("B deinit") } }
在
A的初始化方法中,我们生成了一个
B的实例并将其存储在属性中。然后我们又将
A的实例赋值给了
b.a。这样
a.b和
b.a将在初始化的时候形成一个引用循环。现在当有第三方的调用初始化了
A,然后即使立即将其释放,
A和
B两个类实例的
deinit方法也不会被调用,说明它们并没有被释放。
因为即使
obj不再持有
A的这个对象,b 中的
b.a依然引用着这个对象,导致它无法释放。而进一步,a 中也持有着 b,导致 b 也无法释放。在将
obj设为
nil之后,我们在代码里再也拿不到对于这个对象的引用了,所以除非是杀掉整个进程,我们已经 永远 也无法将它释放了。多么悲伤的故事啊..
在 Swift 里防止循环引用
为了防止这种人神共愤的悲剧的发生,我们必须给编译器一点提示,表明我们不希望它们互相持有。一般来说我们习惯希望 "被动" 的一方不要去持有 "主动" 的一方。在这里 b.a 里对 A 的实例的持有是由 A 的方法设定的,我们在之后直接使用的也是 A 的实例,因此认为 b 是被动的一方。可以将上面的class B的声明改为:
class B { weak var a: A? = nil deinit { println("B deinit") } }
在
var a前面加上了
weak,向编译器说明我们不希望持有 a。这时,当
obj指向
nil时,整个环境中就没有对
A的这个实例的持有了,于是这个实例可以得到释放。接着,这个被释放的实例上对 b 的引用
a.b也随着这次释放结束了作用域,所以
b的引用也将归零,得到释放。添加
weak后的输出:
A deinit B deinit
weak和unowned
在 Swift 中除了weak以外,还有另一个冲着编译器叫喊着类似的 "不要引用我" 的标识符,那就是
unowned。它们的区别在哪里呢?如果您是一直写 Objective-C 过来的,那么从表面的行为上来说
unowned更像以前的
unsafe_unretained,而
weak就是以前的
weak。
用通俗的话说,就是
unowned设置以后即使它原来引用的内容已经被释放了,它仍然会保持对被已经释放了的对象的一个 "无效的" 引用,它不能是 Optional 值,也不会被指向
nil。如果你尝试调用这个引用的方法或者访问成员属性的话,程序就会崩溃。而
weak则友好一些,在引用的内容被释放后,标记为
weak的成员将会自动地变成
nil(因此被标记为 @
weak的变量一定需要是 Optional 值)。
关于两者使用的选择,Apple 给我们的建议是如果能够确定在访问时不会已被释放的话,尽量使用
unowned,如果存在被释放的可能,那就选择用
weak。
日常工作中一般使用弱引用的最常见的场景有两个:
设置
delegate时
在
self属性存储为闭包时,其中拥有对
self引用时
其中,最常见的delegate使用时,可参考我这篇文章:
Swift代理造成内存泄漏的解决办法
所以,weak使用上是更推荐一点的。相关文章推荐
- objective-c中对象所有权的内存管理(关于set,get方法),以及如何使用@property来进行简易操作(九)
- objective-c中对象所有权的内存管理(关于set,get方法),以及如何使用@property来进行简易操作
- SBJSON的使用;OC中接收JSON 数据转到NSString;以及JSON数据和NSDictionary的区别;swift转OC
- vue router 如何使用params query传参,以及有什么区别
- Swift - String与NSString的区别,以及各自的使用场景
- objective-c中对象所有权的内存管理(关于set,get方法),以及如何使用@property来进行简易操作(九)
- Swift - String与NSString的区别,以及各自的使用场景
- onTouch和onTouchEvent以及onClick的顺序,有什么区别,又该如何使用?
- Swift之String与NSString的区别,以及各自的使用场景
- Swift开发必备技巧:内存管理、weak和unowned
- 在编码时如何使用\r与\n,两者的区别
- weak和assign的区别,以及使用场景
- getwidth和getmeasuredwidth的区别以及两者的使用场景
- Swift开发必备技巧:内存管理、weak和unowned
- 什么是抽象类什么是接口两者有什么区别?如何使用它(转载)
- swift 内存管理,WEAK 和 UNOWNED
- Swift里的内存管理:内存管理、weak和unowned
- 何时使用strong、weak以及unowned
- 如何在Swift中使用weak修饰Delegate
- {@link}与@see的简单使用以及区别--如何写好Java代码注释。