Deinit in strict Swift concurrency behaves differently from prior Swift versions and might cause unexpected compile or runtime issues. I’m getting annoyed with it :)
Aside question. How do you deal with potential memory leaks that ban deinit from a call and as a result if you do cleanup in deinit then your resources will be always in memory?
when you hit deinit there is no turning back for that object - it will be deallocated at the end of deinit execution. even if you capture self there or call methods on self it won't prevent it from deallocating so in a way it was "safe" even before. the difference is that before you could strongly save the reference to the object being deallocated from deinit to somewhere else and get a confused state/bad reference situation but why would you do that in the first place!?!? :) and now, with all the compiler strictness, it's just harder to deal with. IMO, as I said in the article - just never use deinit, you don't really need it. Instead, tightly control your "system" (aka the application logic code) so that you know when you remove something from its "final" reference place the resources need to be cleaned up. You just have to be explicit and thoughtful about the business logic of when it happens instead of carelessly relying on deinit to be called for you at some other point in time.
Oh, sorry I mean a case with a strong reference. For example if it’s a class. With your initial approach I see you used to clean some resources in deinit. What if one day some other dev reuse this class and make a strong reference so that old logic with clean logic in deinit will not work as well. If you did something similar - do you have any approaches how to deal with that? Maybe it’s unit testing or some analytics observer or something else
honestly, no approaches that I encountered besides what I described above - change your architecture so that the deinit becomes unnecessary. I'm not a big fan of "defensive" programming in general. In my opinion your architecture/structure should nudge you in the right direction and/or restrict bad things you could do as much as possible.
Aside question. How do you deal with potential memory leaks that ban deinit from a call and as a result if you do cleanup in deinit then your resources will be always in memory?
when you hit deinit there is no turning back for that object - it will be deallocated at the end of deinit execution. even if you capture self there or call methods on self it won't prevent it from deallocating so in a way it was "safe" even before. the difference is that before you could strongly save the reference to the object being deallocated from deinit to somewhere else and get a confused state/bad reference situation but why would you do that in the first place!?!? :) and now, with all the compiler strictness, it's just harder to deal with. IMO, as I said in the article - just never use deinit, you don't really need it. Instead, tightly control your "system" (aka the application logic code) so that you know when you remove something from its "final" reference place the resources need to be cleaned up. You just have to be explicit and thoughtful about the business logic of when it happens instead of carelessly relying on deinit to be called for you at some other point in time.
Oh, sorry I mean a case with a strong reference. For example if it’s a class. With your initial approach I see you used to clean some resources in deinit. What if one day some other dev reuse this class and make a strong reference so that old logic with clean logic in deinit will not work as well. If you did something similar - do you have any approaches how to deal with that? Maybe it’s unit testing or some analytics observer or something else
honestly, no approaches that I encountered besides what I described above - change your architecture so that the deinit becomes unnecessary. I'm not a big fan of "defensive" programming in general. In my opinion your architecture/structure should nudge you in the right direction and/or restrict bad things you could do as much as possible.
And thanks for the post - good catch!
Possibly the hardest issue I've ever debugged was a problem where I kicked off a task in a deinit.
to me it was default implementation for a protocol extension... never again... extensions are banned from my codebases :)