Become a MacRumors Supporter for $50/year with no ads, ability to filter front page stories, and private forums.

Thomas Harte

macrumors 6502
Original poster
Nov 30, 2005
401
19
I'd like to be able to add an observer that is notified when an object is deallocated. Is there a way built-in to Cocoa or the Objective-C runtime to achieve that?

I've had a quick go at key-value observing 'retainCount', but that doesn't work. I'm not sure if NSObject isn't KVO compliant or the instance variable has another name but in either case I think that puts that idea out of play.
 
Retain counts shouldn't be used for anything other than helping out in debugging (and they're not even all that good for that). The count never actually hits 0 because when the count is 1, and the object receives a release message, the object is then dealloced without decrementing the count.

Perhaps you could put a delegate call in your dealloc method, prior to your cleanup.
 
I would not build your application in a way that depends on this. Maybe you can step back and explain what you need to accomplish and we might be able to suggest another way to accomplish it. Checking retainCount is often wrought with peril, and depending on dealloc being called is dicey, as well as it might never be called.

Again: what behavior do you need?

-Lee
 
As I said, retainCount itself is a red herring. It's just the first thing I thought to try to achieve my actual aim of allowing an observer to track when an object is deallocated. I offered retainCount mainly to make it clear that I am trying to find a solution on my own and I thought that mentioning the one solution I had tried would make it clearer what my level of proficiency is. As I said, I have now explicitly ruled out anything based on retainCount. This is not a question particularly about reference counting or the meaning of retainCount.

If you really want the waffle rather than simply the question, then:

I have a type of resource. It costs a lot to create and/or to store. Lots of objects use the same resource at the same time.

In this particular project, there's a factory class that fetches or creates the resources. That factory class implements a resource cache.

It is extremely inconvenient and, I think, a bad design to have to call a specific method on the factory when one of the other objects out there in the wild wants to release a resource. If you need to be specific about it, objects may have no reference to a resource other than in an array or a dictionary, so resources will naturally be released and dealloced in the normal way by the standard Foundation classes. Having to step through things and look for objects of the relevant type, then call a special type of 'release' on them clearly isn't a good solution.

My current implementation has the cache not retaining the objects (in the memory counting sense) and resources giving a shout to it from dealloc.

That works fine, except that now resources have to know that they're cached. It adds an extra layer of who needs to know about who else, complicating the program flow and the object graph.

Since observing for changes or events is a standard Objective-C pattern through KVO and notification centres respectively, I was hoping there was some way to automatically observe for a particular object being deallocated. The cache observes the resources, the resources neither know nor care whether they're being used in a project with or without a cache. They just work.

Obviously if there's no such thing then I'll have to stick with the resources explicitly giving a call out on dealloc. I can generalise it to a delegate with formal protocol situation to achieve proper modularity, but it's not as neat as I was hoping for.

@lee1210: if you genuinely believe that "depending on dealloc being called is dicey, as well as it might never be called" then I think you're doing something wrong, though I take your point about retainCount.
 
@lee1210: if you genuinely believe that "depending on dealloc being called is dicey, as well as it might never be called" then I think you're doing something wrong, though I take your point about retainCount.

He's not doing anything wrong. The OS doesn't call dealloc for all objects when your application is terminated. You are even supposed to call a function telling the OS that your app can be killed anytime without warning (for example when the user shuts down their machine) if you don't need to save any state.
 
He's not doing anything wrong. The OS doesn't call dealloc for all objects when your application is terminated. You are even supposed to call a function telling the OS that your app can be killed anytime without warning (for example when the user shuts down their machine) if you don't need to save any state.

Ah, yes, I've done something wrong in assuming too much knowledge of my use scenario by over trimming my original question.

In my case I have one object in a program that doesn't want to incorrectly believe that another object in the same program is still valid/in memory once it no longer is. Obviously in that case, using dealloc as a trigger is explicitly correct.

To be clear: saving state explicitly isn't a consideration. Having the whole program silently booted from memory is no problem.
 
He's not doing anything wrong. The OS doesn't call dealloc for all objects when your application is terminated. You are even supposed to call a function telling the OS that your app can be killed anytime without warning (for example when the user shuts down their machine) if you don't need to save any state.

Thanks to gnasher729 for jumping in, but to hammer this home:
"You should not tie management of system resources to object lifetimes ... When an application terminates objects may not be sent a dealloc message"
from http://developer.apple.com/mac/library/documentation/Cocoa/Conceptual/MemoryMgmt/MemoryMgmt.pdf, page 16.

As for doing something wrong, I'm really not "doing" to much personally, I just try to help. The dealloc issue has come up a number of times, and has been a point of confusion, so I thought I'd mention it.

As for your specific issue, I've read your second post a few times, but I'm not 100% sure I grok what's going on. Is the case that your "resource handler" is acting as a proxy to a scarce resource, but it does not control all access to the resource, it just hands out a reference to it? And where you're running into trouble is determining when the resource is no longer in use, so it can be disposed of (from the "proxy"), requiring it be recreated if access is requested? In other words, there will be times in your application's lifecycle when this resource is not needed, and you'd like to be able to dispose of it during these times to free up some memory?

-Lee

Edit: after reading your most recent post, I think I have a better idea of what you're up to. If you're comfortable using dealloc for this, my approach would be:
In your big, cached object add an instance variable that points to the controller/cacher.
In your resource controller, when you allocate your cached object, pass in a reference to the controller itself. If you don't have one already, add a "hasValidAwesomeObject" instance variable that is set to true when you create your awesome object, and a method to set this to false.
In your awesome object's dealloc, call the controller's "set to false" method.
In your controller, you will always check your hasValidAwesomeObject member before handing out a reference. If it's false, instantiate an awesome object, set hasValidAwesomeObject to true, and return the reference to the new awesome object.

Edit 2: maybe this is already your approach mentioned in your second post, and you're just looking for something cleaner/more automagical. If so, I can't think of one.
 
Not only is retainCount unreliable, so is dealloc in a way. Objects can live for an undetermined amount of time after their retain count goes to zero.

If you want to know when an object is actually dealloced, that is probably as easy as putting something like...

[[NSNotificationCenter defaultCenter] postNotificationName: @"ObjectDealloced" object: [self description]];

Into the object's dealloc method, and

[[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(someObjectDidDealloc: ) name: @"ObjectDealloced" object: nil];

Into whatever object wants to keep track of that dealloc.

Depending on what you're trying to achieve could you just have an NSLog() in your dealloc method?
 
Thanks to gnasher729 for jumping in, but to hammer this home:
"You should not tie management of system resources to object lifetimes ... When an application terminates objects may not be sent a dealloc message"
from http://developer.apple.com/mac/library/documentation/Cocoa/Conceptual/MemoryMgmt/MemoryMgmt.pdf, page 16.

As for doing something wrong, I'm really not "doing" to much personally, I just try to help. The dealloc issue has come up a number of times, and has been a point of confusion, so I thought I'd mention it.
As I said, I was in the wrong. And, it seems, gracelessly so.

As for your specific issue, I've read your second post a few times, but I'm not 100% sure I grok what's going on. Is the case that your "resource handler" is acting as a proxy to a scarce resource, but it does not control all access to the resource, it just hands out a reference to it? And where you're running into trouble is determining when the resource is no longer in use, so it can be disposed of (from the "proxy"), requiring it be recreated if access is requested? In other words, there will be times in your application's lifecycle when this resource is not needed, and you'd like to be able to dispose of it during these times to free up some memory?
That's exactly it. With the additional observation that this is library code and the resource actually may or may not be cached depending on the project. So, specific pecific issues are that:

  • once they've got the resource, other objects shouldn't have to care where they got it from;
  • the resource should work with or without a cache and, logically, there's a design advantage to resources not having to care whether they're cached as it doesn't affect how they operate generally;
  • the cache wants to keep track of resources but doesn't want to keep them around if nobody else is still using them. Which at the point of implementation means keeping pointers to objects without retaining them and needing to know when those pointers become invalid.

Therefore, all I want to know is whether there is a way built into the runtime to observe when an instance of an object is deallocated. If not then I'll go for a delegate protocol or, I guess, a notification post. For the purposes of neatness and maintainability I want to avoid imputing any specific knowledge of the cache into the resource.
 
Not only is retainCount unreliable, so is dealloc in a way. Objects can live for an undetermined amount of time after their retain count goes to zero.

If you want to know when an object is actually dealloced, that is probably as easy as putting something like...

[[NSNotificationCenter defaultCenter] postNotificationName: @"ObjectDealloced" object: [self description]];

Into the object's dealloc method, and

[[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(someObjectDidDealloc: ) name: @"ObjectDealloced" object: nil];

Into whatever object wants to keep track of that dealloc.

Depending on what you're trying to achieve could you just have an NSLog() in your dealloc method?

A notification of some sort would solve the problem, even though I'm currently designing around the assumption that there'll be only one listener. However, it's also an assumption that the same listener is the only one that'll be interested so I don't think there's a reason to say that either notifications or a delegate protocol beat the other from the point of view of not over-engineering. Notifications are one to many, but a delegate approach would in theory allow every resource instance to have a different delegate. And memory isn't so constrained that a single delegate for all instances of the class through a class method and a static global can be rescued from being ugly and atypical.

This isn't just a question of design, it's of maintainability too. It's meant to be library code and it'll all be used in a multiple developer environment, so both are important.
 
"Therefore, all I want to know is whether there is a way built into the runtime to observe when an instance of an object is deallocated. If not then I'll go for a delegate protocol or, I guess, a notification post. For the purposes of neatness and maintainability I want to avoid imputing any specific knowledge of the cache into the resource."

Ah, well, bollocks. I'd say it wouldn't hurt to "pollute" the resource with this. When there is no resource controller/cacher, the pointer to such in the resource would be nil, so the message to say the resource is invalid in dealloc does nothing; no harm, no foul. If that is no good, oh well.

As for a built-in method for observing an object's lifecycle, there is none that I know of.

Also, no harm done on the "doing it wrong" bit. I only point it out for completeness sake. You've obviously been around the block and know what's what. This is not the case with everyone posting.

-Lee
 
This isn't just a question of design, it's of maintainability too. It's meant to be library code and it'll all be used in a multiple developer environment, so both are important.

Umm... so NSNotification is too general and a delegate is too specific...

And.. other developers don't know about either paradigms?

For one thing, library code really shouldn't concern the client with the implementation details of the library code.

Either way....
[[NSNotificationCenter defaultCenter] removeObserver:self name:ThisThread object:nil];
 
Thanks to all who have helped.

Umm... so NSNotification is too general and a delegate is too specific...
Neither is too anything; but that doesn't mean it's not worth discussing the positives and negatives of approaches before picking one. The best solution is almost always the simplest one, that typically being the one that most directly expresses your intentions.

And.. other developers don't know about either paradigms?

For one thing, library code really shouldn't concern the client with the implementation details of the library code.
It being a library means that I shouldn't expose implementation specifics through the interface. Since I'm a fallible human being, it doesn't mean I can assume that nobody else will ever need to read or to adjust the implementation.

Sticking to established patterns and to code that states your intentions with as limited an overview as possible is a good way to avoid bugs and to ensure that any you do create are easily fixed by somebody else. Ensuring that your code can be fixed by somebody else also reduces scheduling constraints on projects.
Either way....
[[NSNotificationCenter defaultCenter] removeObserver:self name:ThisThread object:nil];
My original post makes it clear that I already have a working solution to the problem and was merely enquiring about a potential better solution. The various posts above both by myself and others make it clear that we all accept that to be one other solution. Nobody is saying that you're wrong.
 
I know the answer! Or at least something that looks like it.

Do you know about objc_setAssociatedObject(id1, void*, id2, OBJC_ASSOCIATION_RETAIN) ?

What it does is register an object to be id2 to be associated with object id1 under key void*. This means two things: 1) id2 can be retrieved later from id1 by the same void* key 2) id2 will be deallocated if id1 was deallocated.

So one thing you can do is create a special "watcher" object that does the right thing in the destructor, for example post to NSNotificationCenter. Then you add an instance of this special "watcher" object to any other object whose death you may wish to observe.

So there's that. However, I'm not sure you can rely on id2 being deallocated at the same time id1 is deallocated, so there might by a time range when id1is dead while id2 is still not. I would probably not rely on this for production code, however this is a great way to catch and debug hanging references - whenever you have a weak reference you can also register for the death notifications of the target object and throw up a meaningful error/assert exactly when the problem happens, instead of crashing with a stupid sigsegv 5 minutes later.

Okay.

The other option that I'm sure will work much more reliably in terms of timing than that is called "swizzling". You could "swizzle" (i.e. replace at runtime) the NSObject's deallocate method, do your thing from there and then call original deallocate to make sure things get cleaned up. Now the problem is that you're effectively doing something in all object's deallocate, so this could be dangerous in all kinds of ways I can't predict. Maybe you can figure out a way to narrow it down. Swizzling is quite complex and myself I wouldn't dare, but you might. :)
 
Do you know about objc_setAssociatedObject(id1, void*, id2, OBJC_ASSOCIATION_RETAIN) ?

What it does is register an object to be id2 to be associated with object id1 under key void*. This means two things: 1) id2 can be retrieved later from id1 by the same void* key 2) id2 will be deallocated if id1 was deallocated.

So one thing you can do is create a special "watcher" object that does the right thing in the destructor, for example post to NSNotificationCenter. Then you add an instance of this special "watcher" object to any other object whose death you may wish to observe.

So there's that. However, I'm not sure you can rely on id2 being deallocated at the same time id1 is deallocated, so there might by a time range when id1is dead while id2 is still not. I would probably not rely on this for production code, however this is a great way to catch and debug hanging references - whenever you have a weak reference you can also register for the death notifications of the target object and throw up a meaningful error/assert exactly when the problem happens, instead of crashing with a stupid sigsegv 5 minutes later.

Okay.

The other option that I'm sure will work much more reliably in terms of timing than that is called "swizzling". You could "swizzle" (i.e. replace at runtime) the NSObject's deallocate method, do your thing from there and then call original deallocate to make sure things get cleaned up. Now the problem is that you're effectively doing something in all object's deallocate, so this could be dangerous in all kinds of ways I can't predict. Maybe you can figure out a way to narrow it down. Swizzling is quite complex and myself I wouldn't dare, but you might. :)

this is absolutely brilliant, thanks

it's a simple, low cost, configurable way to check that stuff gets deallocated.

in my testing it seemed to get deallocated as soon as the host object was deallocated.

it's fantastic because Instruments has never worked for me, and still doesn't work - I don't know what's wrong with it but every time I start it it has some issue. POS.
 
The thread is two years old, but why not just use a singleton? Isn't that exactly what you want?

So have a class that has a class variable that points to an instance of itself if one exists, or to NULL if it doesn't. Then when some other part of your program needs to access it you do

Code:
[Class defaultInstanceOfClass];

And that class method will either return the one existing instance of that class if there is one already, or it will create an instance of the class, assign it to the pointer, and return it, thus allowing future calls to the class to return the same instance rather than allocate another one.

This is the way things like NSUserDefaults (standardDefaults) and NSNotificationCenter (defaultCenter) work.

I believe the part that most people get tripped up on is how to actually make a class variable, given that's not a common task. I'm not quite certain about it myself, but I think the way that's done is:

Code:
@static Class* defaultInstanceOfClass
 
And that class method will either return the one existing instance of that class if there is one already, or it will create an instance of the class, assign it to the pointer, and return it, thus allowing future calls to the class to return the same instance rather than allocate another one.

Have a look at dispatch_once to make this both efficient and thread-safe.
 
Register on MacRumors! This sidebar will go away, and you'll see fewer ads.