Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

objective-c: relying on dealloc

Is it a legitimate practice to rely on a deterministic dealloc (ex: for clean-up)?

Since ARC, and even manual reference counting, is inherently deterministic, I was wondering what other people thought about relying on dealloc getting called immediately (relatively, considering autoreleasepool).

In other modern programming languages, like C#, a dispose-like pattern is employed when you need deterministic clean-up. And I would imagine Obj-C with garbage collection encourages this behavior, as well.

So, with that said, an example would be a UIViewController which cancels outstanding operations in dealloc, rather than trying to program around the sometimes frustrating semantics of viewDidDisappear.

Another example would be a stream object that implicitly opens and closes in init and dealloc, respectively, rather than requiring open or close to be called.

Since Apple has deprecated GC, I would imagine that these sorts of patterns won't be broken anytime soon, and they are incredibly handy, though I can't find any documentation on whether this should be encouraged.

like image 469
xtravar Avatar asked Oct 20 '25 14:10

xtravar


2 Answers

You are absolutely correct, you can rely on dealloc being called relatively soon after the last reference is released (manually or though ARC, it does not matter). Unlike GC where the finalizer is called when the system has some free time, or in some cases is never called, dealloc gets called very reliably. Apple allows and even encourages using this pattern by suggesting that we should perform all of our resource clean-up tasks inside dealloc.

This does not mean, however, that you should rely on dealloc exclusively. For example, take a look at the NSStream class: it offers you an explicit close method, letting you force closing the stream at will, without waiting for the call of dealloc to happen. This is a very good pattern to follow in case the resource is very expensive (file handles, semaphores, etc.): the primary mechanism for releasing these resources should be a separate close method. The dealloc method should release the resource as well, but it should also issue a warning, informing your that you missed a call of close.

like image 102
Sergey Kalinichenko Avatar answered Oct 22 '25 04:10

Sergey Kalinichenko


Regardless of your memory management system, tying expensive resources (e.g. files & sockets, images, views, large memory allocations, etc) to object lifetimes is risky. Even if you're manually retaining & releasing, you might unwittingly retain an object somewhere and forget about it (or otherwise delay its release unnecessarily). ARC makes it even more likely that these things will happen, since it's much less apparent where the retains come from, and when the corresponding releases take effect. And of course GC makes it all completely indefinite.

Generally for expensive and/or limited resources you should try to follow a sole-ownership pattern. Yes, you can still retain/release as normal to prevent premature deallocation, but the dominant owner should be well defined and responsible for clearing out the object when it's done - e.g. calling invalidate, or close, or whatever is appropriate. This makes perfect sense in a lot of the common cases - you generally know when you're done with a file or a socket, for example, so even if they happen to be encapsulated inside some wrapper class, you should just close them explicitly.

In some cases this can also help find bugs that would otherwise remain hidden, or be hard to track down. For example, if your file wrapper class raises an exception when read or write are called after the file is closed, you'll soon catch those cases. Whereas if you didn't close the file when you thought you were done with it, the reads and writes would just happen as usual and you might not notice that your file has unexpected data in it.

You can also use the same principles to break retain cycles.

like image 23
Wade Tregaskis Avatar answered Oct 22 '25 04:10

Wade Tregaskis