1

Hi I have a FriendsViewController where I display my friends records fetched from coreData. I have another View Controller AddFriendViewController Which is presented by FriendsViewController to add a new friend the and it saves the the Context in it. I am listening to this notification of changes on the shared MOC in my FriendsViewController.

 [[NSNotificationCenter defaultCenter]
 addObserverForName:NSManagedObjectContextDidSaveNotification
 object:appdelegate.context queue:nil 
 usingBlock:^(NSNotification * _Nonnull note) {
                    NSLog(@"Re-Fetch Whole Friends Array from core data and Sort it with UILocalizedIndexedCollation and  reloadData into table");
                }];

In AddFriendsViewController just create a friend object and i

Friend *friend= [NSEntityDescription 
                insertNewObjectForEntityForName:@"Friend" 
                inManagedObjectContext:appdelegate.context];
friend.name=nameTextfield.text;
[appdelegate.context save:&error];
[self.navigationController popViewControllerAnimated:YES];

Now when I perform save on the context from a AddFriendViewController the above block in FriendsViewController is triggered couple of times instead of one time which cause more processing because re-fetching whole data from core data.I cannot use Fetched Results Controller because I am using UILocalizedIndexedCollation to sort my array into section. So my question is why it is being called twice or sometimes even thrice? Or is there any alternative for this ?

Asadullah Ali
  • 1,056
  • 14
  • 31
  • Are you by any chance using more than one managed object context, with a parent/child relationship between them? – Tom Harrington Oct 16 '15 at 20:15
  • I have figured it out that I have to remove this observer I am adding this observer in the didLoad but don't know where to remove it? PS. I have one view controller behind this view controller in stack so every time I go back and push again to this view it adds another observer. – Asadullah Ali Oct 16 '15 at 22:16

3 Answers3

1

Only you know when you want the notification observer to be active.

However, two common paradigms are:

If you want to be notified anytime during the life of the view controller, then you register the observer in viewDidLoad and remove the observer in dealloc.

If you want to be notified anytime the view is active, you register the observer in viewWillAppear and remove in viewWillDisappear.

EDIT

I used this statement to remove all entries [[NSNotificationCenter defaultCenter]removeObserver:self]; And it was still showing same behaviour. Then I used addObserver: selector: name: object: method which worked. But Why the other one was not removed ? – Asadullah Ali

That's because you added a block-based observer, which returns an observer object. You remove the object it returned to you. You really should read the documentation of each method that you use.

If you use the block-observer method, the add/remove will look like this.

id observer = [[NSNotificationCenter defaultCenter]
    addObserverForName:SomeNotification
                object:objectBeingObserved
                 queue:nil
            usingBlock:^(NSNotification *note) {
    // Do something
}];

[[NSNotificationCenter defaultCenter] removeObserver:observer];

If you use the selector-observer method, you need to remove the observer that you provide to the add call.

[[NSNotificationCenter defaultCenter]
    addObserver:someObject
       selector:@selector(notificationHandler:)
           name:SomeNotification
         object:objectBeingObserved];

[[NSNotificationCenter defaultCenter]
    removeObserver:someObject
              name:SomeNotification
            object:objectBeingObserved];
Jody Hagins
  • 27,943
  • 6
  • 58
  • 87
  • I have noticed observer added with this method addObserverForName: object: queue: usingBlock: is not removed in dealloc method. – Asadullah Ali Oct 16 '15 at 23:50
  • I used this statement to remove all entries [[NSNotificationCenter defaultCenter]removeObserver:self]; And it was still showing same behaviour. Then I used addObserver: selector: name: object: method which worked. But Why the other one was not removed ? – Asadullah Ali Oct 16 '15 at 23:56
  • Yes this one sums up. Thanks – Asadullah Ali Oct 17 '15 at 14:03
1

First, I would strongly suggest using a NSFetchedResultsController instead of building your own observer.

Second, sounds like you are adding the observer several times. You should add it in the -viewDidLoad and remove it in -dealloc.

Again, a NSFetchedResultsController is a better solution. You will have better performance and avoid refetching like you are doing now.

Marcus S. Zarra
  • 46,571
  • 9
  • 101
  • 182
  • I would love to use NSFetchedResultsController as I am using it in other TableViewControllers or ViewControllers. But I could not use it here because I have to support names in different languages (Chinese,English in my case ) to be sorted With SectionIndexTitle I have tried hard but ended up doing this way. It would be highly appreciated if you can suggest some other way to use FRC for Chinese,English sorting. Ref: http://stackoverflow.com/questions/7199934/nsfetchedresultscontroller-v-s-uilocalizedindexedcollation – Asadullah Ali Oct 16 '15 at 23:40
  • And I have noticed observer added with this method addObserverForName: object: queue: usingBlock: is not removed in dealloc method. – Asadullah Ali Oct 16 '15 at 23:48
  • The block observer has some issues. I do not recommend using it. – Marcus S. Zarra Oct 17 '15 at 03:43
  • Is there any other issue also. Because the one I was facing is clarified below by jody – Asadullah Ali Oct 17 '15 at 14:02
0

You must get rid of the observer (as others already have stated here). The easiest way would be to use a "one-time observer" which removes itself when activated. In your example code this would be something like:

id __block observer;
observer = [[NSNotificationCenter defaultCenter]
            addObserverForName:NSManagedObjectContextDidSaveNotification
            object:[PubStore sharedStore].managedObjectContext queue:nil            
            usingBlock:^(NSNotification * _Nonnull notification) {
                [[NSNotificationCenter defaultCenter] removeObserver:observer];
                 NSLog(@"Re-Fetch Whole Friends Array from core data and Sort it with UILocalizedIndexedCollation and  reloadData into table");
            }];

Note that you must store the observer in a __block variable to be able to use it inside the block executed when the observer is triggered.

vilmoskörte
  • 291
  • 1
  • 10