The following example illustrates how to unregister someObserver for all previously registered notifications. This is safe to do in the dealloc method, but you shouldn’t use it otherwise (use removeObserver(_:name:object:)instead).
This is true, but that would also require all subclasses that want to mark themselves as 'safe' in this regard to redeclare dealloc. Anyway, the attribute above wasn't necessarily a serious proposal, just a stand-in for the idea that individual subclasses could indicate "I don't mess with the default UIView/UIViewController behavior of calling dealloc on the main thread."
It would not solve our problems (FB10320292). We often use a composition pattern to avoid the fragile base class, in that case a lot of our UIView "implementations" exist in some objects that do not inherit UIKit types but is owned by them and isolated to the main actor.
After scanning the relevant threads, I think I am missing some key information that would explain the constrains on the solution space:
Are non-isolated deinit really so common? I did a quick scan and about 15% of our deinitializers are easily isolated, either because they only reference let ivars or they do some advanced synchronization internally. The common case is not having a deinitializer, when we write one we are mostly doing something worth isolating. So the goldilocks scenario of nonisolated deinit feels unusual, but maybe there is variation between codebases on this point.
The fact that extending the lifetime myself is UB is surprising because there seem to be no compile-time checks for it (FB10320292). I had grown accustomed to the Swift compiler catching memory issues, so it took me awhile to track down where the EXC_BAD_ACCESS was coming from. This seems like an area where, to the extent we don't support it, we need better diagnostics on the memory side.
Let me focus on this and ask the same question I asked above, slightly differently: what bad would happen if UIView / UIViewController deinits were called on a background thread? And a followup question: what makes these types special compared to a type of my own?
As I can see here the only special thing about UIView/UIViewController (subclass) is that "it is common" they do something in their dealloc that's main thread only... hence we do this "last release special treatment" in the OS to combat this common situation... and thus reduce the number of crashes in users' apps significantly... right? hmm... I can write this class, would that be "too unusual" to ignore?
class Foo { // not a UIView or a UIViewController
deinit {
let v = UIView() // or any other main-thread only thing
print(v)
}
}
IMHO, whatever we do in this area should handle 99.999% cases... not "the most typical" 95% cases. Last release treatment is a hack that highlights a problem, and frankly, looks like a quick band-aid approach rather than a proper solution. I'd rather see we create a proper solution that allows removing that hack, than build some partial solution around that hack.