Swift开发必备技巧:内存管理、weak和unowned
因为 Playground 本身会持有所有声明在其中的东西,因此本节中的示例代码需要在 Xcode 项目环境中运行。在 Playground 中可能无法得到正确的结果。 不管在什么语言里,内存管理的内容都很重要,所以我打算花上比其他 tip 长一些的篇幅仔细地说说这块内容。 Swift 是自动管理内存的,这也就是说,我们不再需要操心内存的申请和分配。当我们通过初始化创建一个对象时,Swift 会替我们管理和分配内存。而释放的原则遵循了自动引用计数 (ARC) 的规则:当一个对象没有引用的时候,其内存将会被自动回收。这套机制从很大程度上简化了我们的编码,我们只需要保证在合适的时候将引用置空 (比如超过作用域,或者手动设为 但是,所有的自动引用计数机制都有一个从理论上无法绕过的限制,那就是循环引用 (retain cycle) 的情况。 什么是循环引用 虽然我觉得循环引用这样的概念介绍不太应该出现在这本书中,但是为了更清晰地解释 Swift 中的循环引用的一般情况,这里还是简单进行说明。假设我们有两个类
class A {
let b: B
init() {
b = B()
b.a = self
}
deinit {
println("A deinit")
}
}
B {
var a: A? = nil
"B deinit")
}
}
在
func application(application: UIApplication!,didFinishLaunchingWithOptions launchOptions: NSDictionary!)
-> Bool
{
// Override point for customization after application launch.
var obj: A? = A()
obj = nil
// 内存没有释放
return true
}
因为即使 在 Swift 里防止循环引用 为了防止这种人神共愤的悲剧的发生,我们必须给编译器一点提示,表明我们不希望它们互相持有。一般来说我们习惯希望 "被动" 的一方不要去持有 "主动" 的一方。在这里 b.a 里对 A 的实例的持有是由 A 的方法设定的,我们在之后直接使用的也是 A 的实例,因此认为 b 是被动的一方。可以将上面的 B {
weak nil
deinit {
"B deinit")
}
}
var a前面加上了 A deinit
B deinit
可能有心的朋友已经注意到,在 Swift 中除了 我们结合实际编码中的使用来看看选择吧。日常工作中一般使用弱引用的最常见的场景有两个:
前者是 Cocoa 框架的常见设计模式,比如我们有一个负责网络请求的类,它实现了发送请求以及接收请求结果的任务,其中这个结果是通过实现请求类的 protocol 的方式来实现的,这种时候我们一般设置
// RequestManager.swift
RequestManager: RequestHandler {
requestFinished() {
"请求完成")
}
sendRequest() {
let req = Request()
req.delegate = self
req.send()
}
}
// Request.swift
@objc protocol RequestHandler {
optional requestFinished()
}
Request {
var delegate: RequestHandler!;
send() {
// 发送请求
// 一般来说会将 req 的引用传递给网络框架
}
gotResponse() {
// 请求返回
delegate?.requestFinished?()
}
}
闭包和循环引用 另一种闭包的情况稍微复杂一些:我们首先要知道,闭包中对任何其他元素的引用都是会被闭包自动持有的。如果我们在闭包中写了 Person {
let name: String
lazy var printName: ()->() = {
"The name is (self.name)")
}
init(personName: String) {
name = personName
}
"Person deinit self.name)")
}
}
var xiaoMing: Person = Person(personName: "XiaoMing")
xiaoMing.printName()
true
}
// 输出:
// The name is XiaoMing
printName是 var printName: ()->() = {
[self] in
if let strongSelf = self {
(strongSelf.name)")
}
}
现在内存释放就正确了: // The name is XiaoMing
// Person deinit XiaoMing
如果我们可以确定在整个过程中 这种在闭包参数的位置进行标注的语法结构是将要标注的内容放在原来参数的前面,并使用中括号括起来。如果有多个需要标注的元素的话,在同一个中括号内用逗号隔开,举个例子: // 标注前
{ (number: Int) -> Bool in
//...
// 标注后
{ [unowned self,weak someObject] (number: true
} (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |