Getting notified when an object instance is deallocated

Discussion in 'Mac Programming' started by Thomas Harte, Jul 25, 2010.

  1. Thomas Harte macrumors 6502

    Joined:
    Nov 30, 2005
    #1
    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.
     
  2. GorillaPaws macrumors 6502a

    GorillaPaws

    Joined:
    Oct 26, 2003
    Location:
    Richmond, VA
    #2
    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.
     
  3. lee1210 macrumors 68040

    lee1210

    Joined:
    Jan 10, 2005
    Location:
    Dallas, TX
    #3
    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
     
  4. Thomas Harte thread starter macrumors 6502

    Joined:
    Nov 30, 2005
    #4
    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.
     
  5. gnasher729 macrumors P6

    gnasher729

    Joined:
    Nov 25, 2005
    #5
    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.
     
  6. Thomas Harte thread starter macrumors 6502

    Joined:
    Nov 30, 2005
    #6
    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.
     
  7. lee1210 macrumors 68040

    lee1210

    Joined:
    Jan 10, 2005
    Location:
    Dallas, TX
    #7
    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.
     
  8. jared_kipe macrumors 68030

    jared_kipe

    Joined:
    Dec 8, 2003
    Location:
    Seattle
    #8
    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?
     
  9. Thomas Harte thread starter macrumors 6502

    Joined:
    Nov 30, 2005
    #9
    As I said, I was in the wrong. And, it seems, gracelessly so.

    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.
     
  10. Thomas Harte thread starter macrumors 6502

    Joined:
    Nov 30, 2005
    #10
    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.
     
  11. lee1210 macrumors 68040

    lee1210

    Joined:
    Jan 10, 2005
    Location:
    Dallas, TX
    #11
    "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
     
  12. jared_kipe macrumors 68030

    jared_kipe

    Joined:
    Dec 8, 2003
    Location:
    Seattle
    #12
    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];
     
  13. Thomas Harte thread starter macrumors 6502

    Joined:
    Nov 30, 2005
    #13
    Thanks to all who have helped.

    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.

    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.
    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.
     
  14. DenNukem macrumors member

    DenNukem

    Joined:
    Jul 12, 2008
    Location:
    Seattle
    #14
    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. :)
     
  15. orthorim macrumors 6502

    Joined:
    Feb 27, 2008
    #15
    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.
     
  16. ArtOfWarfare macrumors 604

    ArtOfWarfare

    Joined:
    Nov 26, 2007
    #16
    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
     
  17. gnasher729 macrumors P6

    gnasher729

    Joined:
    Nov 25, 2005
    #17
    Have a look at dispatch_once to make this both efficient and thread-safe.
     

Share This Page