KVO: UITableViewCell model property - how to unregister when the model is canceled?

I have a UITableViewCell that has an imageView that loads a UIImage from a myObject model object. The UIImage property on myObject loaded asynchronously, and I watch it change from tableViewCell to know when I can reload it (and replace the placeholder image). Here is my problem: seenario:

  • The user performs a search that fills an array of model objects.
  • These model objects are represented in UITableViewCell s, and the iconImg property is observed to complete the loading of async. (This works great, bye).
  • If the user performs another search, I discard these original model objects, and this leads to the fact that "an instance of the Merchant class was released, and messages about the key value were still registered with it", which is not very good.

The dealloc methods for my UITableViewCell unregister the cell, but this is not called when I dealloc base model in the above script. Is there a clean way to tell my cells to unregister when an observed instance of dealloc s? Can I just delete all observers in a dealloc model object?

Side question: why does KVO automatically remove registered observers from an object when it dealloc s?

+6
objective-c uitableview key-value-observing
source share
3 answers

UPDATE: see my comment for a better way to fix this. However, to understand what is happening, this question is still informative ... (final update)

Solved (at least in my case). Here's what happens:

My view controller is used to display search results and saves these search results as an array of model objects. I use a custom subclass of UITableViewCell to display each search result and, as part of its configuration, store the associated model object (search result) inside the property of the custom cell, name it myCell.modelObject .

As I already noted, an error occurred at every search, except the original one. For debugging, I set a symbolic breakpoint on NSKVODeallocateBreak and saw that this happens when reusing one of my user cells:

 - (void)configureWithModelObject:(ModelObject*)aModelObject { // @property (nonatomic, retain) ModelObject *modelObject; self.modelObject = aModelObject; // <-- NSKVODeallocateBreak paused here .... } 

So, my view controller, having received the second set of search results, will free an array containing the source objects of the search model. Those that are still stored by my reusable instances of user cells will adhere, at least until these cells are reused again. At this point, when self.modelObject is updated, the previous modelObject is again freed and finally freed , but since the cell that observed it was not freed but was reused instead, my [modelObject removeObserver ...] call in [myCell dealloc] has not been called.

Solution: When setting up my custom cell, I need to check if the model object is installed, which is a sign of reuse of this cell. If so, I stop observing the original modelObject before updating the property:

 - (void)configureWithModelObject:(ModelObject*)aModelObject { if(modelObject != nil) { // I'm being re-used! Stop observing old model object! [modelObject removeObserver:self forKeyPath:@"keyPathIWasObserving"]; } self.modelObject = aModelObject; // <-- NSKVODeallocateBreak paused here .... 

Note I still need to call [modelObject removeObserver ...] in [cell dealloc] to handle the situation when the view manager itself is freed.

I hope this is useful to others with a similar pattern.

Best Steve

+5
source share

I suspect that you are not saving the model object in the cell, because otherwise it will not be released before the cell will be able to refuse it. As Stephen Poletto noted, some code will help you understand your problem.

Why doesn't KVO automatically remove registered observers from an object when it is canceled?

Because only the facility and the notification center know about the subscription, but

1) The notification center does not know when the dealloc 'ed object

2) the object knows, but imagine that every object in the system checks whether it has been subscribed to something or something has been subscribed to it. There will be a big loss in performance. (I did this in home bindings, but it only ran on classes whose instances were signed at least once for a small set of objects)

We are on a device with limited resources, and we must clear everything that we did explicitly and in a timely manner.

+1
source share

I think what you really want is a call to observeValue... in your user instance of a UITableView cell with the key, for example @"self.modelObject" . Thus, you only need to unregister if the cell is dealloc . If necessary, you can include the properties of the model object in the key path, for example @"self.modelObject.name" , if you do not receive the necessary notifications when changing individual properties of the model object.

0
source share

All Articles