2

In a NSManagedObject Sub Class I have the code …

- (void) awakeFromInsert { 
[self addObserver:[NSApp delegate] forKeyPath:@"name" options:NSKeyValueObservingOptionNew context:nil]; 
}

Which adds my App Delegate as an Observer, what I want to do now is from inside my App Delegate, I want to remove itself as an Observer for my NSManagedObject Sub Class.

How would I do this? Thanks.

I was thinking of adding this to my App Delegate

[JGManagedObject removeObserver:self forKeyPath:@"name"];

but unfortunately removeObserver:forKeyPath: is not a Class Method.

Joshua
  • 15,200
  • 21
  • 100
  • 172

4 Answers4

4

For something like this, it's probably best to rethink the design. The delegate, in this case, would have to have some specific knowledge of the managed object itself in order to do this -- and the delegate would have to have some idea about when in the lifecycle it should (or would want to) stop observing the object.

You have a few choices. Instead of doing this in awake from insert, you could have the delegate start observing it when it creates it and then stop observing it when it gives up ownership. If that is not feasible in your design, you could have the object remove its observer when it is deallocated. If this is a fire-and-forget (basically the delegate only cares once), you could remove the observer after the first change notification. Since, however, you created the observation within the creation lifecycle of this object, it is probably best to remove that observation at the destruction of the object:

- (void)dealloc
{
  [self removeObserver:[NSApp delegate] forKeyPath:@"name"];
  // other clean-up
  [super dealloc];
}

You might also want to do this when the object awakes from fetch and from fault and release the observer when the object will become a fault.

Jason Coco
  • 77,985
  • 20
  • 184
  • 180
  • Removing the observer in `-dealloc` won't work for Garbage Collected apps, though. – Abizern Oct 14 '09 at 09:59
  • And also it's not fire-and-forget, the delegate needs to do it more than once. – Joshua Oct 14 '09 at 15:12
  • Yeah, it will work if you're not using GC -- good catch Abizem. If you are using GC then I suggest reworking this so that the delegate registers itself as the observer when it inserts or fetches the objects and then removes itself when it is finished with the object. – Jason Coco Oct 14 '09 at 16:05
  • You could also just add it to your finalize method instead of your dealloc method if you are using GC – Jason Coco Oct 14 '09 at 16:08
  • Basically I have an IBAction in my App Delegate and when it starts I want to remove the App Delegate as an observer and at the end I want to add the App Delegate as an observer of The NSManagedObject sub class again. Does this help you understand my situation? – Joshua Oct 14 '09 at 16:36
  • Ah, so just do that. In beginning of your method send the object the remove observer message: [obj removeObserver:[NSApp delegate] forKeyPath:@"name"]; then just add it again at the end of your method: [obj addObserver:[NSApp delegate] forKeyPath:@"name" options:NSKeyValueObservingOptionNew context:nil]; – Jason Coco Oct 15 '09 at 15:44
1

Much the same way you added the observer in the first place, only with fewer options:

// Given some managed object "object"...
[object removeObserver:self forKeyPath:@"name"];

Note that we remove self as the observer, rather than the application delegate as given by [NSApp delegate], since the code will be running within the delegate itself.

Tim
  • 59,527
  • 19
  • 156
  • 165
  • But the Observer isn't observing the App Delegate it's observing the Managed Object Sub Class. – Joshua Oct 14 '09 at 06:55
  • Tim - this is wrong; this would remove observations of the delegate for "name" changes on the delegate -- which were never being observed in the first place (delegate.name). You have to remove observation of the managed object itself. – Jason Coco Oct 14 '09 at 06:58
  • So do I replace `object` with `JGManagedObject`(My NSManagedObject Subclass)? I don't think I would because JGManagedObject is a Class. – Joshua Oct 14 '09 at 17:43
  • No - this method relies on your app delegate having an *instance* of `JGManagedObject`. So you would somehow need to have an object declared as `JGManagedObject *object = ...` before that line of code is valid. – Tim Oct 14 '09 at 21:39
  • But there really isn't anything to put after the `=` is there? Could you not in the Header file just add `JGManagedObject *object`? – Joshua Oct 15 '09 at 05:38
  • If you do that, then add the appropriate `@property` and `@synthesize` directives, then set the property to the appropriate object (something like `[[NSApp delegate] setObject:self]` from the JGManagedObject where you register the observer), then yes, you won't need that first line, and can just remove the observer as listed in my answer. – Tim Oct 15 '09 at 07:12
  • Ok. So I would also add `@property (nonatomic, retain) JGManagedObject *object;` and `@synthesize object`. However when I do this I get an error, http://grab.by/9Jq. – Joshua Oct 15 '09 at 15:13
  • Make sure your NSApp delegate knows that `JGManagedObject` is a class - either `#import` the `JGManagedObject.h` header to the delegate's header, or add a `@class` declaration to the delegate header and import the managed object header in the delegate's implementation file. – Tim Oct 15 '09 at 16:20
  • Ok. I've done that, so this is what it looks like now … http://grab.by/9KG http://grab.by/9KF http://grab.by/9KE but I still get an error http://grab.by/9KJ. – Joshua Oct 15 '09 at 16:27
  • Joshua - the @synthesize directive goes in the implementation file. And anyway, this isn't going to work for you because you're not watching one object, but lots of them. – Abizern Oct 15 '09 at 17:42
  • What do you mean that I'm not watching one object but lots of them? – Joshua Oct 15 '09 at 18:05
  • If I make the (not unrealistic) assumption that this question is based on the trouble you are having here http://stackoverflow.com/questions/1548291 then it's plain to see that you are not watching a single object but lots of objects of class JGManagedObject. You're trying to add a property, object, but what is this going to represent? – Abizern Oct 15 '09 at 18:44
  • Object is supposed to represent the JGManagedObject class. Basically I should be Key Value Observing the CD 'name' property of all the objects in my Tree Controller, so that when the 'name' property of one of my objects changed it triggers an action. – Joshua Oct 16 '09 at 05:35
0

How about sending your object the removeObserver:forKeyPath message just before you delete it from the ManagedObjectContext?

Abizern
  • 146,289
  • 39
  • 203
  • 257
0

From iOS 11 and above, we have automatic registration of KVO. From the Foundation Release Notes for macOS 10.13 and iOS 11

Relaxed Key-Value Observing Unregistration Requirements Prior to 10.13, KVO would throw an exception if any observers were still registered after an autonotifying object's -dealloc finished running. Additionally, if all observers were removed, but some were removed from another thread during dealloc, the exception would incorrectly still be thrown. This requirement has been relaxed in 10.13, subject to two conditions:

• The object must be using KVO autonotifying, rather than manually calling -will and -didChangeValueForKey: (i.e. it should not return NO from +automaticallyNotifiesObserversForKey:) • The object must not override the (private) accessors for internal KVO state

If all of these are true, any remaining observers after -dealloc returns will be cleaned up by KVO; this is also somewhat more efficient than repeatedly calling -removeObserver methods.

aios
  • 405
  • 5
  • 14