我一直关注Playground中 Intermediate Swift WWDC会话中的弱引用示例。我稍微修改了代码如下:
class Apartment {
let address: Int
init(address: Int) {
self.address = address
}
weak var tenant: Person?
}
class Person {
let name: String
init(name: String){
self.name = name
}
weak var home: Apartment?
func moveIn(apt: Apartment) {
self.home = apt
apt.tenant = self
}
}
var renters = ["John Appleseed": Person(name: "John Appleseed")]
var apts = [16: Apartment(address: 16)]
renters["John Appleseed"]!.moveIn(apts[16]!)
renters["John Appleseed"] = nil // memory should be released here
// then apts[16].tenant should be nil
if let tenantName = apts[16]!.tenant?.name {
// this should only execute if the Person object is still in memory
println("\(tenantName) lives at apartment number \(apts[16]!.address)")
} else {
// and this line should execute if the memory is released as we expect
println("Nobody lives at apartment number \(apts[16]!.address)")
}
// Console output in Playground: John Appleseed lives at apartment number 16
// Console output in standalone app: Nobody lives at apartment number 16
根据我对弱引用的理解,当从renters字典中删除时,应该释放为Person实例分配的内存,因为对它的唯一其他引用是弱的。但是,如果程序作为独立的命令行应用程序而不是在Playground中运行,则程序的输出会有所不同。(参见注释)。
答案 0 :(得分:5)
我认为顶级功能(REPL / playground)保持强大的参考,以促进交互行为,并在帧返回时进行清理。此行为消除了交互式环境中的内存泄漏。
我复制了 Viktor 的简单示例并使用了xcrun swift
REPL。
在REPL模式下,我将逻辑包装在一个函数中,它按预期工作。如果/当你关心内存被清理时,我建议将你的逻辑包装在一个函数中。
// declaration of the types
class Person {
let name: String
weak var home: Apartment?
init(pName: String){
name = pName
}
}
class Apartment {
let postalCode: Int
init(pPostalCode: Int) {
postalCode = pPostalCode
}
}
func testArc() {
// create Person object
var personJulius: Person = Person(pName: "Julius")
// create Apartment object
var apartmentBerlin: Apartment? = Apartment(pPostalCode: 10777)
// connect Apartment object and Person object
personJulius.home = apartmentBerlin
// Set only strong reference of Apartment object to nil
apartmentBerlin = nil
// Person object should now have nil as home
if personJulius.home != nil {
println("Julius does live in a destroyed apartment")
} else {
println("everything as it should")
}
}
//outputs "everything as it should"
testArc()
答案 1 :(得分:2)
我猜Playground本身保留了对该对象的强引用,所以代码的行为有所不同?如果是这种情况,这可能会导致一些意想不到的问题!
答案 2 :(得分:2)
我尝试了一点不那么复杂的代码设置。 但在 Playground 文件中确实存在同样的问题,但在真正的命令行项目中。
在命令行项目中,输出应该的所有内容,而在操场上它是 Julius确实住在被毁坏的公寓中。
import Cocoa
// declaration of the types
class Person {
let name: String
weak var home: Apartment?
init(pName: String){
name = pName
}
}
class Apartment {
let postalCode: Int
init(pPostalCode: Int) {
postalCode = pPostalCode
}
}
// create Person object
var personJulius: Person = Person(pName: "Julius")
// create Apartment object
var apartmentBerlin: Apartment? = Apartment(pPostalCode: 10777)
// connect Apartment object and Person object
personJulius.home = apartmentBerlin
// Set only strong reference of Apartment object to nil
apartmentBerlin = nil
// Person object should now have nil as home
if personJulius.home != nil {
println("Julius does live in a destroyed apartment")
} else {
println("everything as it should")
}
答案 3 :(得分:0)
这不仅是弱参考。在操场上,deinit不起作用。由于将变量设置为nil不能让deinit运行,所以不是弱引用应该工作的时间。
class MyClass {
init() {
println("ready")
}
deinit {
println("OK")
}
}
var aClass: MyClass?
aClass
aClass = MyClass()
aClass = nil
答案 4 :(得分:0)
在Xcode 10.1 Playgrounds中,我可以确认deinit的行为仍然很奇怪,并且我不能使用Playgrounds来测试事物是否已正确释放。 更新: 从另一个类似的线程中,我了解到Xcode> New> Project> macOS> Command Line Tool是创建通用测试环境的相对轻量级的方法,该环境可以很好地用于测试释放。
class Person {
let name: String
init(named name: String) { self.name = name }
var house: House?
deinit { print("\(name) is being deinitialized") }
}
class House {
let address: String
init(address: String) { self.address = address }
weak var tenant: Person?
deinit { print("House \(address) is being deinitialized") }
}
func move(_ person: Person, into house: House){
house.tenant = person
person.house = house
}
当Person和House断开连接时,deinits可以正常工作。
但是,如果我将Buffy移入房屋并删除Buffy,则由于租户很弱,应该取消初始化Buffy对象并将租户设置为nil。如您所见,两者均未发生。
即使我删除了房屋(第38行),也都没有取消初始化。 弱引用在游乐场中的行为类似于强引用。 在此示例中,将运行代码包装在函数中不会更改任何内容。