Search code examples
objective-cmessagingkey-value-observingnsnotificationcenter

Many to one relationship messaging, KVO or NSNotification


Could someone please advise me on best or practical way of creating a many to one relationship messaging system. I am new to Objective-C so if this something that I "should know" as an Obj-C developer, please feel free to point me in the right direction on tutorials/documentation.

Currently, I'm working on a Brick Breaker game to learn and get better at Obj-C/Cocos2D/Box2D. I am trying to create an internal messaging system in my BrickMgr class that holds instances of bricks (NSMutableArray). When a brick is destroyed, I want to notify my parent(BrickMgr) the score value of the brick, so that it can then decide how to use or communicate it to the heads-up-display(HUD).

From all the Googling/reading I've done, it seems like KVO or NSNotificationCenter would be the way to go, but all the examples I've read are one-to-many relationship. I am wondering could I do the opposite and use it in the form of many-to-1 relationship.

E.g.: In each instance of my Brick class, when the brick is destroyed I can do

//Brick class, when brick.state = BRICK_DESTROYED
[NSNotificationCenter defaultCenter] postNotificationName:BB_SCORE_CHANGED_NOTIFICATION object:self userInfo:nil];

and in my BrickManager class register my observer to listen to the postNotification

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onScoreChanged:) name:BB_SCORE_CHANGED_NOTIFICATION object:nil];

Please advise.


Solution

  • NSNotifications are most useful for to-many relations, where they need to notify multiple objects of a change. Your implementation looks good (you'll also need to unregister for the notification inside dealloc for instance).

    As long as it's a to-one relation, you can also use delegates (since each object - brick - will need to notify 1 single object).

    You could have for instance:

    // Brick.h
    @class Brick;
    
    @protocol BrickDelegate <NSObject>
    -(void)brickStateChanged:(Brick *)sender;
    // .. some other methods
    @end
    
    @interface Brick : NSObject {
        id<BrickDelegate>    _delegate;
    }
    
    @property(nonatomic, assign) id<BrickDelegate>    delegate;
    @end
    
    // Brick.m
    @implementation Brick
    @synthesize delegate=_delegate;
    
    ...
    
    -(void)setState:(int)newState{
        if(_state==newState) {
            return;
        }
        _state=newState;
        [self.delegate brickStateChanged:self];
    }
    
    ...
    @end
    

    Somewhere in another class:

    // in the init method for instance
    _bricks = [[NSMutableArray alloc] init];
    Brick *b = [[Brick alloc] init];
    b.delegate = self;
    [_brocks addObject:[b autorelease]];
    
    -(void)brickStateChanged:(Brick *)sender {
        // handle state changed for the brick object
    }
    
    -(void)dealloc{
        // reset the delegates - just in case the Brick objects were retained by some other object as well
        [_bricks enumerateObjectsUsingBlock:^(Brick *b, NSUInteger idx, BOOL *stop){
            b.delegate = nil;
        }];
        [_bricks release];
        [super dealloc];
    }