ahmad
ahmad

Reputation: 1252

Perform a selector only once when receiving two or more notifications

I am trying to perform a selector when certain events occur such as:

  1. the app becomes active
  2. the internet reachability status changes from not reachable to reachable

I am posting a notification when these events occur with the following code

[[NSNotificationCenter defaultCenter] postNotificationName:Notif_Name object:nil];

and I want to perform a selector when the notification is received in the same UIViewController instance, so I'm registering it as an observer in viewDidLoad

[[NSNotificationCenter defaultCenter] addObserverForName:Notif_Name object:nil queue:nil usingBlock:^(NSNotification *note) {

    [self performSelectorOnMainThread:@selector(selectorName) withObject:nil waitUntilDone:NO];
}];

Now, these events I'm observing may happen simultaneously. How do I make sure that my selector is performed only once?

Upvotes: 1

Views: 419

Answers (3)

jscs
jscs

Reputation: 64002

Coalescing of notifications is supported by NSNotificationQueue in Cocoa Touch.

Instead of using the notification center to post the notification directly, enqueue the notification and tell the queue to combine similar or identical notifications. You can have it match based on either or both of the notification name and sender. You're not providing an object for the notification, so you can only coalesce using the name.

NSNotification * note = [NSNotification notificationWithName:Notif_Name object:nil]

[[NSNotificationQueue defaultQueue] enqueueNotification:note
                                           postingStyle:NSPostASAP
                                           coalesceMask:NSNotificationCoalescingOnName
                                               forModes:nil];

Upvotes: 2

André Slotta
André Slotta

Reputation: 14030

you could also try the following:

[[NSNotificationCenter defaultCenter] addObserverForName:Notif_Name object:nil queue:nil usingBlock:^(NSNotification *note) {
  [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(selectorName) object:nil];
  [self performSelector:@selector(selectorName) withObject:nil afterDelay:1.0];
}];

you wait a second until you perform the action and cancel all pending requests...

Upvotes: 0

Guilherme Amantea
Guilherme Amantea

Reputation: 41

You can keep a flag telling you whether selectorName has been executed and protect it against multithreaded access.

@interface ViewController ()
@property (nonatomic) BOOL executed;
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(executeSelector) name:Notif_Name object:nil];
}

- (void)executeSelector {
    @synchronized (self) {
        if (!self.executed) {
            self.executed = YES;
            [[NSNotificationCenter defaultCenter] removeObserver:self];
            [self performSelectorOnMainThread:@selector(selectorName) withObject:nil waitUntilDone:NO];
        }
    }
}

@end

Upvotes: 0

Related Questions