|
|
|
#import "NSObject+MHChannels.h" |
|
|
|
@interface MHChannelListener : NSObject |
|
|
|
@property (nonatomic, weak) id object; |
|
@property (nonatomic, copy) MHChannelsBlock block; |
|
@property (nonatomic, assign) NSInteger priority; |
|
@property (nonatomic, assign) dispatch_queue_t queue; |
|
|
|
@end |
|
|
|
@implementation MHChannelListener |
|
|
|
@synthesize object; |
|
@synthesize block; |
|
@synthesize priority; |
|
@synthesize queue; |
|
|
|
- (NSString *)description |
|
{ |
|
return [NSString stringWithFormat:@"%@ object = %@", [super description], object]; |
|
} |
|
|
|
@end |
|
|
|
@implementation NSObject (MHChannels) |
|
|
|
- (NSMutableDictionary *)mh_channelsDictionary |
|
{ |
|
static dispatch_once_t pred; |
|
static NSMutableDictionary *dictionary; |
|
dispatch_once(&pred, ^{ dictionary = [NSMutableDictionary dictionaryWithCapacity:4]; }); |
|
return dictionary; |
|
} |
|
|
|
- (void)mh_pruneDeadListenersFromChannel:(NSString *)channelName |
|
{ |
|
NSMutableDictionary *channelsDictionary = [self mh_channelsDictionary]; |
|
NSMutableArray *listeners = [channelsDictionary objectForKey:channelName]; |
|
|
|
NSMutableSet *listenersToRemove = nil; |
|
|
|
for (MHChannelListener *listener in listeners) |
|
{ |
|
if (listener.object == nil) |
|
{ |
|
if (listenersToRemove == nil) |
|
listenersToRemove = [NSMutableSet set]; |
|
|
|
[listenersToRemove addObject:listener]; |
|
} |
|
} |
|
|
|
if (listenersToRemove != nil) |
|
{ |
|
for (MHChannelListener *listener in listenersToRemove) |
|
[listeners removeObject:listener]; |
|
|
|
if ([listeners count] == 0) |
|
[channelsDictionary removeObjectForKey:channelName]; |
|
} |
|
} |
|
|
|
- (void)mh_post:(NSDictionary *)dictionary toChannel:(NSString *)channelName |
|
{ |
|
NSParameterAssert(channelName != nil); |
|
|
|
NSMutableDictionary *channelsDictionary = [self mh_channelsDictionary]; |
|
@synchronized (channelsDictionary) |
|
{ |
|
NSMutableArray *listeners = [channelsDictionary objectForKey:channelName]; |
|
if (listeners != nil) |
|
{ |
|
for (MHChannelListener *listener in listeners) |
|
{ |
|
if (listener.object != nil) |
|
{ |
|
if (listener.queue == nil) |
|
listener.block(listener, dictionary); |
|
else |
|
dispatch_async(listener.queue, ^{ listener.block(listener, dictionary); }); |
|
} |
|
} |
|
|
|
[self mh_pruneDeadListenersFromChannel:channelName]; |
|
} |
|
} |
|
} |
|
|
|
- (void)mh_listenOnChannel:(NSString *)channelName block:(MHChannelsBlock)block |
|
{ |
|
[self mh_listenOnChannel:channelName priority:0 queue:nil block:block]; |
|
} |
|
|
|
- (void)mh_listenOnChannel:(NSString *)channelName priority:(NSInteger)priority queue:(dispatch_queue_t)queue block:(MHChannelsBlock)block |
|
{ |
|
NSParameterAssert(channelName != nil); |
|
NSParameterAssert(block != nil); |
|
|
|
NSMutableDictionary *channelsDictionary = [self mh_channelsDictionary]; |
|
@synchronized (channelsDictionary) |
|
{ |
|
NSMutableArray *listeners = [channelsDictionary objectForKey:channelName]; |
|
if (listeners == nil) |
|
{ |
|
listeners = [NSMutableArray arrayWithCapacity:2]; |
|
[channelsDictionary setObject:listeners forKey:channelName]; |
|
} |
|
|
|
MHChannelListener *listener = [[MHChannelListener alloc] init]; |
|
listener.object = self; |
|
listener.block = block; |
|
listener.priority = priority; |
|
listener.queue = queue; |
|
|
|
[listeners addObject:listener]; |
|
[self mh_pruneDeadListenersFromChannel:channelName]; |
|
|
|
[listeners sortUsingComparator:^(MHChannelListener *obj1, MHChannelListener *obj2) |
|
{ |
|
if (obj1.priority < obj2.priority) |
|
return NSOrderedDescending; |
|
else if (obj1.priority > obj2.priority) |
|
return NSOrderedAscending; |
|
else |
|
return NSOrderedSame; |
|
}]; |
|
} |
|
} |
|
|
|
- (void)mh_removeFromChannel:(NSString *)channelName |
|
{ |
|
NSParameterAssert(channelName != nil); |
|
|
|
NSMutableDictionary *channelsDictionary = [self mh_channelsDictionary]; |
|
@synchronized (channelsDictionary) |
|
{ |
|
NSMutableArray *listeners = [channelsDictionary objectForKey:channelName]; |
|
if (listeners != nil) |
|
{ |
|
for (MHChannelListener *listener in listeners) |
|
{ |
|
if (listener.object == self) |
|
listener.object = nil; |
|
} |
|
|
|
[self mh_pruneDeadListenersFromChannel:channelName]; |
|
} |
|
} |
|
} |
|
|
|
- (void)mh_debugChannels |
|
{ |
|
NSMutableDictionary *channelsDictionary = [self mh_channelsDictionary]; |
|
@synchronized (channelsDictionary) |
|
{ |
|
NSLog(@"Channels dictionary: %@", channelsDictionary); |
|
} |
|
} |
|
|
|
@end |