Swift playground中的弱引用无法按预期工作

时间:2014-06-06 12:35:57

标签: swift

我一直关注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中运行,则程序的输出会有所不同。(参见注释)。

5 个答案:

答案 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运行,所以不是弱引用应该工作的时间。 deinit does not run

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可以正常工作。 enter image description here

但是,如果我将Buffy移入房屋并删除Buffy,则由于租户很弱,应该取消初始化Buffy对象并将租户设置为nil。如您所见,两者均未发生。

enter image description here

即使我删除了房屋(第38行),也都没有取消初始化。 弱引用在游乐场中的行为类似于强引用。 在此示例中,将运行代码包装在函数中不会更改任何内容。