0

I've set up a Singleton class. Several view controllers will be getting data from the singleton for their views. The data is periodically updated.

How would I set up the app so that the view controllers watch the singleton to see if there are changes in data... or the singleton sends a message to a view if there is new data relevant to that view?

cannyboy
  • 24,180
  • 40
  • 146
  • 252

3 Answers3

5

You can use NSNotificationCenter to post notifications whenever the singleton's data changes (such as setter methods) and have any associated views register to receive the notifications.

http://developer.apple.com/library/mac/#documentation/Cocoa/Reference/Foundation/Classes/nsnotificationcenter_Class/Reference/Reference.html

TigerCoding
  • 8,710
  • 10
  • 47
  • 72
  • http://stackoverflow.com/questions/2191594/how-to-send-and-receive-message-through-nsnotificationcenter-in-objective-c – TigerCoding Nov 11 '11 at 15:04
1

Create a protocol with some methods and add those listeners (they have to implement the protocol) to an array. If you change something inside your singleton, just call an all listeners this method.

For example:

@protocoll SingletonListenerProtocol
- (void)singleton:(Singleton*)singleton didChangeValueX:(NSString*)newValue;  
@end


-------------------

@implementation Singleton
- (void)setValueX:(NSString*)newX {
  valueX = newX;
  for(id<SingletonListenerProtocol> listener in listeners) {
    [listener singleton:self didChangeValueX:newX];
  }
}
@end
1

I would use key-value observing. It’s built in, so it’s easy to take advantage of.

  1. If your data source class already uses properties, you’re all set. Here is an example of one which does:

    @interface Source : NSObject {}
    @property (nonatomic, retain) NSString *value;
    @end
    
    @implementation Source
    @synthesize value;
    @end
    

    If it does not, you only need to define a couple of specially-named methods to make it work:

    - (NSDictionary*)someValue;
    - (void)setSomeValue: (NSDictionary*)newValue;
    

    (where someValue is the name of your property, and NSDictionary* is its type. setSomeValue does not need to be a public method).

    If a property is computed (so that it’s not appropriate to have a setter for it), you can trigger KVO notifications manually like this:

    [self willChangeValueForKey:@"someValue"];
    // Do work with changes the effective value of someValue
    [self didChangeValueForKey:@"someValue"];
    
  2. Then, any object may subscribe to notifications when a key changes, like this:

    @implementation Observer
    - (id)initWithSource: (Source *)source
    {
        if ([super init]) {
            _source = source;
            [_source addObserver:self forKeyPath:@"value" options:NSKeyValueObservingOptionNew context:NULL];
        }
        return self;
    }
    - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
    {
        NSLog(@"New value: %@", [change objectForKey:NSKeyValueChangeNewKey]);
    }
    - (void)dealloc
    {
        [_source removeObserver:self forKeyPath:@"value"];
        [super dealloc];
    }
    @end
    

    Your observer must implement a observeValueForKeyPath:ofObject:change:context: method, which will be called when any observed key changes on any observed object. You can tell what changed by looking at the arguments passed at ofObject: and KeyPath:.

  3. Change some values! Here's how the process would work with these example classes:

    Source *source = [Source new];
    [(Observer*)[Observer alloc] initWithSource: source];
    source.value = @"foo";
    

    You’ll see this on the console, logged by observer:

    New value: foo

The Key Value Coding and Key Value Observing programming guides are pretty good. I would also take a look at the NSKeyValueObserving protocol reference.

s4y
  • 50,525
  • 12
  • 70
  • 98