2

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).

Jakub Truhlář
  • 20,070
  • 9
  • 74
  • 84

1 Answers1

4

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()
  }
}
Hamish
  • 78,605
  • 19
  • 187
  • 280
  • Thanks, I was wondering if this is some kind of `defer` statement misuse but I guess not. A defer statement defers execution until the current scope is exited, so for me - it is not inside the scope in the time of execution. And the suggested replacement is indeed unfortunate. It seems like a flaw. – Jakub Truhlář Mar 27 '19 at 22:43
  • @JakubTruhlář Not quite – [from the Swift language guide](https://docs.swift.org/swift-book/ReferenceManual/Statements.html#grammar_defer-statement): "*A `defer` statement is used for executing code just before transferring program control outside of the scope that the defer statement appears in*" – Hamish Mar 27 '19 at 22:45
  • Good point, would you consider the fact it triggers observers in the`init`/`deinit` a bug then? I guess the formulation of the defer part in the docs has changed - based on the open bug [SR-1437](https://bugs.swift.org/browse/SR-1437) – Jakub Truhlář Mar 27 '19 at 23:04
  • 1
    @JakubTruhlář I wouldn't really consider it a bug given that changing the behaviour now would be quite source breaking, as quite a few people rely on using `defer` to trigger property observers, and it doesn't quite meet the bar of "causing active harm" to warrant changing IMO. It's certainly unfortunate that we're stuck with this behaviour though. – Hamish Mar 27 '19 at 23:04
  • Thank you for filing the bug report [SR-10207](https://bugs.swift.org/browse/SR-10207) @Hamish. – Jakub Truhlář Mar 28 '19 at 00:50