Weak references in Swift playground don't work as expected

前端 未结 5 1458
刺人心
刺人心 2020-11-29 10:44

I have been following the weak referencing example from the Intermediate Swift WWDC session in a Playground. I modified the code slightly as follows:



        
相关标签:
5条回答
  • 2020-11-29 10:50

    I tried an a bit less complex setup of the code. But did have the same problem in the Playground file, but not in a real command line project.

    In a command line project, the output was everything as it should, and in the playground it was Julius does live in a destroyed apartment.

    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")
    }
    
    0 讨论(0)
  • 2020-11-29 10:50

    It is not only weak reference. In playground, the deinit does not work. Since set a variable to nil can not let deinit run, it is not the time weak reference should work. deinit does not run

    class MyClass {
        init() {
            println("ready")
        }
        deinit {
            println("OK")
        }
    }
    var aClass: MyClass?
    aClass
    aClass = MyClass()
    aClass = nil
    
    0 讨论(0)
  • 2020-11-29 10:55

    I believe the top-level function (REPL/playground) is keeping a strong reference to facilitate interactive behavior, and cleaning up when the frame returns. This behavior eliminates memory leaks in the interactive environment.

    I copied Viktor's simple example and used the xcrun swift REPL.

    In REPL mode, I wrapped the logic in a function and it works as expected. If/when you care when the memory is cleaned up, I would suggest wrapping your logic in a function.

    // 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()
    
    0 讨论(0)
  • 2020-11-29 11:00

    Update 3: As of 11.3.1: Playground seems to be deiniting objects as expected, as far as I can tell. My original and out of date answer follows: In Xcode 10.1 Playgrounds, I can confirm that deinits are still behaving strangely and I can't use Playgrounds to test whether things are being properly deallocated. Update 1: From another similar thread, I learned that Xcode>New>Project>macOS>Command Line Tool, is a relatively lightweight way to create a generic testing environment that works fine for testing deallocation.

    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
    }
    

    When Person and House are unconnected, deinits work properly.

    However, if I move Buffy into the house, and delete Buffy, because tenant is weak, the Buffy object should be deinited and tenant set to nil. As you can see neither happens.

    Even after I delete house (line 38), neither are deinited. Weak references are behaving like strong references in the Playground. Wrapping the Run code in a function does not change anything in this example. Update 2: In Xcode 11, As wbennet suggests above, if you wrap your run code in a func and call it, deallocations work for weak references as defined, in Playground.

    0 讨论(0)
  • 2020-11-29 11:06

    I guess that the Playground itself keeps a strong reference to the object, so the code behaves differently? If that's the case, this could cause some unexpected problems!

    0 讨论(0)
提交回复
热议问题