-
Notifications
You must be signed in to change notification settings - Fork 25
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Rewrite ARTEventEmitter per the spec and adjust Connection. #179
Conversation
I didn't run the Objective-C test suite. Sorry. Working on that now. |
fa9ebd1
to
63d0cc7
Compare
// This way you can automatically "implement the EventEmitter pattern" for a class | ||
// as the spec say. It's supposed to be used together with ART_EMBED_IMPLEMENTATION_EVENT_EMITTER | ||
// in the implementation of the class. | ||
#define ART_EMBED_INTERFACE_EVENT_EMITTER(EventType, ItemType) - (__GENERIC(ARTEventListener, ItemType) *)on:(EventType)event call:(void (^)(ItemType))cb;\ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why did you not use a protocol?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well, I'm thinking about it and maybe it is not possible using objc protocols with generics?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can protocols implement methods and have properties? I think they were just interfaces that implementors had to, well, implement themselves.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, you can have properties. I guess you can have the protocol for the interface and have the macro for the implementation.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In any case, a protocol suggests that classes can (should?) bring their own implementation for the protocol, when in fact that's not the case here. In this case, they should just provide access to the ARTEventEmitter
implementation.
I'd prefer not to overcomplicate the thing with a protocol besides the class.
@tcard yes, that is correct. FYI, we have an unspecified equivalent of how this used to work though, see https://github.com/ably/ably-js/blob/master/common/lib/util/eventemitter.js#L160-L176 and https://github.com/ably/ably-ruby/blob/master/lib/ably/modules/state_emitter.rb#L61-L103. Obviously this is out of spec so not needed |
- (instancetype)initWithRealtime:(ARTRealtime *)realtime; | ||
- (id<ARTSubscription>)on:(ARTRealtimeConnectionStateCb)cb; | ||
- (void)removeEvents; | ||
- (__GENERIC(ARTEventListener, ItemType) *)onAll:(void (^)(ItemType))cb; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Based on our previous discussions about method overloading, I thought we had preferred to use overloading in the end. If so, why do we have onAll
as opposed to on(void (^)(ItemType))cb
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Overloading is problematic when there is a different number of arguments. Plus, I think we agreed on doing the idiomatic thing... See #158 (comment)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I thought we agreed on something quite different, see #121 (comment). I thought #158 was an exception because it was an initializer / constructor?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, you're right, I didn't consider that Objective-C allows overloading with different number of arguments. My previous comment makes no sense. Will change.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed at 8aa38f6.
8aa38f6
to
43f6833
Compare
Rebased, plus two changes: 5cba6ca and 43f6833. |
- (__GENERIC(ARTEventListener, ItemType) *)once:(void (^)(ItemType __art_nullable))cb;\ | ||
\ | ||
- (void)off:(EventType)event listener:(__GENERIC(ARTEventListener, ItemType) *)listener;\ | ||
- (void)off:(__GENERIC(ARTEventListener, ItemType) *)listener; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there not also meant to be another overloaded off
without any params?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(RTE5) EventEmitter#off deregisters a listener for the specified event if given, else it is deregisters that listener for all registrations (global and event-specific)
I understand from that that there is at least a listener parameter.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed at e9f6153997cd248376bd0db611c8e77fad733e08.
LGTM, only thing missing from what I can see is an |
Previously, ARTConnection managed state subscriptions and ARTEventEmitter was just a shell. Now the heavy-lifting of passing events around is done by ARTEventEmitter, which is generic and can be reused by others, and ARTConnection just uses it to emit events and expose its listening interface. This also introduces ARTConnectionStateChange, used in connection state events' callbacks, as the spec says. Something that went away is that starting listening to events dispatched the current event to the callback. Now it doesn't. It starts listening to _changes_ from that point on. I think this makes more sense and is consistent with what you expect from an event emitter. Fixes #133.
Also, remove "ignored" list for total listeners; `off` will just be a no-op if called with a specific event and a total listener. See: ably/docs#82 #179 (comment)
e9f6153
to
cacb6eb
Compare
} | ||
@finally { | ||
for (ARTEventEmitterEntry *entry in toRemoveFromListenersForEvent) { | ||
[listenersForEvent removeObject:entry]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It only removes the occurrence in that array (listenersForEvent
).
I think it should use self.listeners
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hey, good catch. Although I'm not sure what you mean. This should be calling [removeObject:entry fromArrayWithKey:event inDictionary:self.listeners]
, because that handles the case in which self.listeners[event]
is left empty and needs to be deleted to avoid entry leaks. I fixed that at 859e474.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I didn't thought about the special case when I wrote about it but looks great. 👍
LGTM 👍 |
Rewrite ARTEventEmitter per the spec and adjust Connection.
Previously, ARTConnection managed state subscriptions and
ARTEventEmitter was just a shell. Now the heavy-lifting of
passing events around is done by ARTEventEmitter, which is
generic and can be reused by others, and ARTConnection just uses
it to emit events and expose its listening interface.
This also introduces ARTConnectionStateChange, used in connection
state events' callbacks, as the spec says.
Something that went away is that starting listening to events
dispatched the current event to the callback. Now it doesn't. It
starts listening to changes from that point on. I think this
makes more sense and is consistent with what you expect from an
event emitter.
Fixes #133.