squarezw
2/21/2013 - 8:00 PM

PayPal Here Message Bus Implementation

PayPal Here Message Bus Implementation

//
//  PPHMessageBus.m
//  PayPal Here
//
//  Created by Matthew Pavlinsky on 9/11/12.
//  Copyright (c) 2012 PayPal, Inc. All rights reserved.
//

#import "PPHMessageBus.h"

#import <objc/runtime.h>
#import "NINonRetainingCollections.h"



#pragma mark -
#pragma mark Private

// We all share this pointer.
static NSMutableSet *defaultResponders;

////////////////////////////////////////////////////////////////////////////////////////////////////
@interface PPHMessageBus () {
}
@property (nonatomic, assign) NSMutableSet *instanceResponders;
@property (nonatomic, assign) BOOL isTheAtLeastOneVersion;
@end



////////////////////////////////////////////////////////////////////////////////////////////////////
@implementation PPHMessageBus



#pragma mark -
#pragma mark Initialization

+(PPHMessageBus *)any
{
    static dispatch_once_t pred;
    static PPHMessageBus* shared = nil;
    dispatch_once(&pred, ^{ shared = [[self alloc] initShared]; });
    return shared;
}

+(PPHMessageBus*) atLeastOne
{
    static dispatch_once_t pred;
    static PPHMessageBus* shared = nil;
    dispatch_once(&pred, ^{
        shared = [[self alloc] initShared];
        shared.isTheAtLeastOneVersion = YES;
    });
    return shared;
}

+ (PPHMessageBus*)sharedBus {
    static dispatch_once_t pred;
    static PPHMessageBus* shared = nil;
    dispatch_once(&pred, ^{ shared = [[self alloc] initShared]; });
    return shared;
}

-(id)initShared
{
    self = [super init];
    return self;
}

-(id)init
{
    self = [super init];
    if (self) {
        self.instanceResponders = NICreateNonRetainingMutableSet();
    }
    return self;
}

+(void)initialize
{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        defaultResponders = NICreateNonRetainingMutableSet();
    });
}

#pragma mark -
#pragma mark Responder Registration

- (void)registerResponder:(id)responder {
    NSMutableSet *responders = self.instanceResponders ?: defaultResponders;
    @synchronized(responders) {
        [responders addObject:responder];
    }
}

- (void)unregisterResponder:(id)responder {
    NSMutableSet *responders = self.instanceResponders ?: defaultResponders;
    @synchronized(responders) {
        [responders removeObject:responder];
    }
}

-(BOOL)isRegsitered:(id)responder
{
    NSMutableSet *responders = self.instanceResponders ?: defaultResponders;
    @synchronized(responders) {
  	return [responders containsObject:responder];
    }
}

#pragma mark -
#pragma mark Message forwarding

- (void)forwardInvocation:(NSInvocation *)invocation {
    NSMutableSet *responders = self.instanceResponders ?: defaultResponders;
    // Make a copy of the possible responders, so if a new responder is added as a byproduct of the invocation we don't crash.
    NSSet* respondersSnapshot = nil;
    @synchronized(responders) {
        respondersSnapshot = [NSSet setWithSet:responders];
    }
    
    int count = 0;
    for (id responder in respondersSnapshot) {
        if ([responder respondsToSelector:invocation.selector]) {
            [invocation invokeWithTarget:responder];
            count++;
        }
    }
    
    if (self.isTheAtLeastOneVersion && count == 0) {
        NSAssert(FALSE, @"Expected at least one responder, got none. For invocation: %@ %@", invocation.target, NSStringFromSelector(invocation.selector));
    }
}

- (NSSet *)respondersForProtocol:(Protocol *)protocol {
    NSMutableSet *result = [NSMutableSet set];
    
    NSMutableSet *responders = self.instanceResponders ?: defaultResponders;
    @synchronized(responders) {
        for (id responder in responders) {
            if ([responder conformsToProtocol:protocol]) {
                [result addObject:responder];
            }
        }
    }
    
    return result;
}


@end