简介
不管什么语言,内存管理始终是重中之重,所以作为苹果推荐使用的后起之秀Swift
也不例外,Swift
的内存管理是使用的ARC
。
Automatic Reference Counting: Swift uses Automatic Reference Counting (ARC) to track and manage your app’s memory usage. In most cases, this means that memory management “just works” in Swift, and you do not need to think about memory management yourself. ARC automatically frees up the memory used by class instances when those instances are no longer needed.
在iOS5
之前,iOS
的内存管理是MRC
,那时候的内存管理全靠程序猿。iOS5
之后才有了ARC
,也是苹果一大改进我们一般情况下不需要对对象的release进行特别管理(除通知/Timer/Block等)。 iOS ARC和MRC区别。
工作机制
- Swift内存管理和OC一样: 管理引用类型的内存, 不会管理值类型, 值类型不需要管理
- 采用自动引用计数来管理内存, 默认情况下所有的引用都是强引用
- 当有一个强引用指向某一个对象时,该对象的引用计数会自动+1
- 当该强引用消失时,引用计数会自动-1
一般循环引用
- 👆也介绍了,
Swift
使用的是ARC
,它会自动帮助我们管理内存的 - But 我们开发中,经常会出现有耦合的两个类相互持有对方属性,造成循环引用的问题,看👇:
-
Master
->Dog
有一个强引用 -
Dog
->Master
有一个强引用 - 手动给对象置
nil
(没有deinit)
-
// master 主人
class Master: NSObject {
// 持有一条狗
var dog: Dog?
deinit {
print("---------Master deinit---------")
}
}
---------------------------------------------------------------
// Dog 狗狗
class Dog: NSObject {
// 狗狗有个主人
var master: Master?
deinit {
print("---------Dog deinit---------")
}
}
---------------------------------------------------------------
class ViewController: UIViewController {
var d: Dog?
var m: Master?
override func viewDidLoad() {
super.viewDidLoad()
// 狗狗
d = Dog()
// 主人
m = Master()
// 给狗狗个主人
d?.master = m
// 给主人个狗狗
m?.dog = d
// 手动置nil
d = nil
m = nil
}
}
解决问题
-
weak
: 和OC
中的__weak
一样是一个弱引用.当指向的对象销毁时,会自动将指针指向nil
-
unowned
: 和OC
中的__unsafe_unretained
.当对象销毁时依然指向原来的内存地址, 该修饰不能指向nil
unowned
和weak
是有区别的,看看官方的解释:
“If the captured reference will never become nil, it should always be captured as an unowned reference, rather than a weak reference.”
如果该引用不会变成nil
,那么你应该使用unowned
引用,而不是weak
。所以unowned
其实有点儿像Objective - C
里面的unsafe_unretained
。而且这里有一点需要注意:不要使用weak
去修饰一个let
,因为该引用之后会变,所以只能是var
,这一点在官方文档里面也有提示:
“Weak references must be declared as variables, to indicate that their value can change at runtime. A weak reference cannot be declared as a constant.”
// Dog 狗狗
class Dog: NSObject {
// 狗狗有个主人
weak var master: Master?
deinit {
print("---------Dog deinit---------")
}
}
---------------------------------------------------------------
---------Master deinit---------
---------Dog deinit---------
闭包循环引用
在Swift
中的闭包(closure)
和Objective - C
中的block
一样,会引起循环引用,从而导致内存泄露。解决办法👇:
- 使用weak修饰变量, 打破强引用, 因为使用weak修饰的变量有一次变成nil的机会
- 使用[weak self] 修饰闭包原理跟__weak类似, 这样在闭包中使用self, 就是弱引用
- 使用[unowned self ] 修饰闭包, 跟__unsafe_unretained类似, 不安全
class NextViewController: UIViewController {
var completionHandler:((_ name: String)->())?
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = UIColor.purple
view.alpha = 0.4
//0. 循环引用
self.retainMethod()
//1. weak var weakSelf = self weak
self.weakSelfMethod1()
//2. [weak self] 跟OC __weak 相似 [weak self]
self.weakSelfMethod2()
//3. [unowned self] 跟 _unsafe_unretained 类似 不推荐使用
self.unownedMethod()
}
func retainMethod() {
networkWithPath(path: "www.baidu.com", param: ["name":"haha"]) { (name) in
print("\(self.view)")
}
}
func weakSelfMethod1 () {
weak var weakSelf = self
networkWithPath(path: "www.baidu.com", param: ["name":"haha"]) { (name) in
print("\(String(describing: weakSelf?.view))")
}
}
func weakSelfMethod2(){
networkWithPath(path: "www.baidu.com", param: ["name":"haha"]) { [weak self] (name) in
print("\(String(describing: self?.view))")
}
}
func unownedMethod() {
networkWithPath(path: "www.baidu.com", param: ["name":"haha"]) { [unowned self](name) in
print("\(self.view)")
}
}
func networkWithPath(path: String, param:[String: String], completionHandle:@escaping (_ name: String)->())->(){
self.completionHandler = completionHandle
self.completionHandler!("hello")
}
deinit {
print("---------- NextViewController deinit ----------")
}
}