Search code examples
swiftdeinitproperty-observer

Defer statement at the end of deinit produces a warning


Since the Xcode 10.2 (Swift 5) the defer statement at the end of the deinit scope produces:

'defer' statement before end of scope always executes immediately; replace with 'do' statement to silence this warning

Let's take a look at this example:

var foo: String {
    didSet {
        // smt
    }
}

deinit {
    defer { <--- Warning
        foo = bar
    }
}
  • Of course it's possible to get rid of this warning by moving the code from the observer to a method and call it explicitly but…

What's the point of this warning? - Isn't it reasonable to have the defer statement in the deinit? (e.g. to be able to trigger properties' observers).


Solution

  • The warning is correct in that the use of defer here doesn't change the order of execution of your program, which is what the statement is designed for. It is however unfortunate that the suggested replacement otherwise changes the behaviour of your program (filed a bug: SR-10207).

    It's worth noting that the use of defer to trigger a property observer is a bit of a hack that only works because the type checker considers it to be a different context to the deinit body. You can also achieve the same result with a closure expression:

      deinit {
        { foo = bar }()
      }
    

    Ideally, there would be some form of syntax that lets you tell Swift "don't perform a direct-to-storage access here", so that such workarounds aren't necessary, but there isn't currently.

    A less hacky workaround is to pull out the desired logic of the deinitialiser into a separate method, which puts the logic in a context where property accesses are done normally:

    class C {
      var bar = ""
    
      var foo: String {
        didSet {
          // smt
        }
      }
    
      init(foo: String) { self.foo = foo }
    
      private func doDeinit() {
        foo = bar
      }
    
      deinit {
        doDeinit()
      }
    }