Skip to content

Commit

Permalink
Fix: Replace static eventlistener dict with an instance per NSObject.
Browse files Browse the repository at this point in the history
  This is instance was added with objc_setAssociatedObject when the
  first listener was added.
  • Loading branch information
jerolimov committed Sep 29, 2012
1 parent 5f4c9e5 commit f2568df
Show file tree
Hide file tree
Showing 3 changed files with 58 additions and 10 deletions.
33 changes: 23 additions & 10 deletions EventEmitter/EventEmitter.m
Expand Up @@ -16,6 +16,8 @@

#import "EventEmitter.h"

#import <objc/runtime.h>

#pragma mark - EventEmitterListener (currently internal API) protocol

@protocol EventEmitterListener <NSObject>
Expand All @@ -32,6 +34,8 @@ @interface EventEmitterArrayCallbackListener : NSObject <EventEmitterListener>

#pragma mark - EventEmitterListener (currently internal API) implementation

static const char* _EventEmitter_ListenerArray = "EventEmitter_ListenerArray";

@implementation EventEmitterDefaultCallbackListener
@synthesize once;
@synthesize callback;
Expand Down Expand Up @@ -65,8 +69,6 @@ - (void) notify: (NSArray*) data {

@implementation NSObject(EventEmitterListenerHandling)

NSMutableDictionary* eventListeners = nil;

- (void) on:(NSString*) event callback:(EventEmitterDefaultCallback) callback {
[self addListener:[[EventEmitterDefaultCallbackListener alloc] init] callback:callback event:event once:NO];
}
Expand All @@ -88,8 +90,11 @@ - (void) once:(NSString*) event array:(EventEmitterArrayCallback) callback {
*/
- (void) addListener:(NSObject<EventEmitterListener>*) listener callback:(id) callback event:(NSString*) event once:(BOOL) once {

NSMutableDictionary* eventListeners = objc_getAssociatedObject(self, _EventEmitter_ListenerArray);

if (!eventListeners) {
eventListeners = [NSMutableDictionary dictionary];
objc_setAssociatedObject(self, _EventEmitter_ListenerArray, eventListeners, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

NSMutableArray* eventListener = [eventListeners objectForKey:event];
Expand Down Expand Up @@ -132,19 +137,27 @@ - (void) emit:(NSString*) event arguments: (id) arg0, ... {
}

- (void) emit:(NSString*) event array: (NSArray*) array {
for (NSObject<EventEmitterListener>* listener in [self eventListeners:event]) {
NSMutableDictionary* eventListeners = objc_getAssociatedObject(self, _EventEmitter_ListenerArray);
NSMutableArray* eventListener = [eventListeners valueForKey:event];
NSMutableIndexSet* discardedItems = [NSMutableIndexSet indexSet];
NSUInteger index = 0;

for (NSObject<EventEmitterListener>* listener in eventListener) {
[listener notify:array];
if (listener.once) {
[[self eventListeners:event] removeObject:listener];
[discardedItems addIndex:index];
}
index++;
}
}

- (NSMutableArray*) eventListeners:(NSString*) event {
if (!eventListeners) {
return nil;

if (eventListener.count == discardedItems.count) {
[eventListeners removeObjectForKey:event];
if (eventListener.count == 0) {
objc_setAssociatedObject(self, _EventEmitter_ListenerArray, nil, OBJC_ASSOCIATION_ASSIGN);
}
} else if (discardedItems.count > 0) {
[eventListener removeObjectsAtIndexes:discardedItems];
}
return [eventListeners valueForKey:event];
}

@end
32 changes: 32 additions & 0 deletions EventEmitterTests/EventEmitterTests.m
Expand Up @@ -63,4 +63,36 @@ - (void)testSimplestOnceArray {
STAssertEquals(i, 1, @"");
}

- (void)testMemoryWithOnMethod {
int loops = 1000000;

__block int i = 0;
NSObject* emitter;
for (int j = 0; j < loops; j++) {
emitter = [[NSObject alloc] init];
[emitter on:@"key" callback:^(id value) {
i++;
}];
[emitter emit:@"key"];
[emitter emit:@"key"];
}
STAssertEquals(i, loops * 2, @"");
}

- (void)testMemoryWithOnceMethod {
int loops = 1000000;

__block int i = 0;
NSObject* emitter;
for (int j = 0; j < loops; j++) {
emitter = [[NSObject alloc] init];
[emitter once:@"key" callback:^(id value) {
i++;
}];
[emitter emit:@"key"];
[emitter emit:@"key"];
}
STAssertEquals(i, loops, @"");
}

@end
3 changes: 3 additions & 0 deletions README.md
@@ -1,3 +1,6 @@
Node.js inspired EventEmitter for objective c.

http://nodejs.org/docs/latest/api/events.html

Some internals:
http://oleb.net/blog/2011/05/faking-ivars-in-objc-categories-with-associative-references/

0 comments on commit f2568df

Please sign in to comment.