@interface AWEventArgs : NSObject @end
@interface AWEventHandler : NSObject { @private NSString *_description; } @property (nonatomic, assign) id target; @property (nonatomic, assign) SEL method; +(AWEventHandler *)handlerWithTarget:(id)target method:(SEL)method; @end @implementation AWEventHandler @synthesize method, target; -(id)initWithTarget:(id)t method:(SEL)m; { self = [super init]; if(self) { target = t; method = m; _description = [[NSString alloc] initWithFormat:@"EventHandler, Target=%@, Method=%@", NSStringFromClass([target class]), NSStringFromSelector(method)]; } return self; } -(NSString *)description { return _description; } -(void)dealloc { [_description release]; [super dealloc]; } +(AWEventHandler *)handlerWithTarget:(id)target method:(SEL)method { AWEventHandler *handler = [[[AWEventHandler alloc] initWithTarget:target method:method] autorelease]; return handler; } @end
@interface AWEventHandlersList : NSObject { NSMutableArray *_handlers; } @property (nonatomic, copy) NSString *name; -(void)addReceiver:(id)receiver delegate:(SEL)delegate; -(void)removeReceiver:(id)receiver delegate:(SEL)delegate; -(void)clearReceivers; -(void)invoke; -(void)invokeWithSender:(id)sender; -(void)invokeWithSender:(id)sender args:(AWEventArgs *)event; @property (nonatomic, retain) NSRunLoop *runLoop; @end
addReceiver
and removeRecevier
logical - they accept an object and a selector that will accept calls later.invoke
methods should throw an event, passing it to the signed objects for processing. They are given in three variants - in order not to pass empty nil values in the event that there is no need for any event parameters.clearReceivers
method clearReceivers
internal, it is better to define it in an anonymous section, since the calling code should not be able to unsubscribe other objects from events, but historically it has been rendered to the interface. It is easy to fix, if you think it is wrong.runLoop
property runLoop
necessary if you are going to make certain events be tied to a specific thread. For example, this is necessary if some code in the worker thread must update the visual part of the application, or vice versa - from the UI thread should be access to any worker thread synchronized through the message queue, that is, if there is a need to throw events and process them in different streams. @implementation AWEventHandlersList @synthesize runLoop = _runLoop; @synthesize name = _name; -(id)init { self = [super init]; if(!self) return nil; _handlers = [[NSMutableArray alloc] init]; return self; } -(void)addReceiver:(id)receiver delegate:(SEL)delegate { /* , , , * . */ [self removeReceiver:receiver delegate:delegate]; [_handlers addObject:[AWEventHandler handlerWithTarget:receiver method:delegate]]; } -(void)removeReceiver:(id)receiver delegate:(SEL)delegate { /* , , * (NSLock), , , * , NSLock */ for(AWEventHandler *handler in [[_handlers copy] autorelease]) if(handler.method == delegate && handler.target == receiver) [_handlers removeObject:handler]; } -(void)clearReceivers { [_handlers removeAllObjects]; } -(void)invoke { [self invokeWithSender:nil args:nil]; } -(void)invokeWithSender:(id)sender { [self invokeWithSender:sender args:nil]; } -(void)invokeWithSender:(id)sender args:(AWEventArgs *)event { [self invokeWithSender:sender args:event runLoop:_runLoop]; } -(void)invokeWithSender:(id)sender args:(AWEventArgs *)event runLoop:(NSRunLoop *)runLoop { /* , , null * */ if(!runLoop) runLoop = [NSRunLoop currentRunLoop]; NSUInteger order = 1; NSArray *handlersCopy = [NSArray arrayWithArray:_handlers]; for(AWEventHandler *handler in handlersCopy) if(runLoop == [NSRunLoop currentRunLoop]) [self internalInvoke:[NSArray arrayWithObjects:handler, sender == nil ? [NSNull null] : sender, event == nil ? [NSNull null] : event, nil]]; else [runLoop performSelector:@selector(internalInvoke:) target:self argument:[NSArray arrayWithObjects:handler, sender == nil ? [NSNull null] : sender, event == nil ? [NSNull null] : event, nil] order:order++ modes:[NSArray arrayWithObject:NSDefaultRunLoopMode]]; } /* performSelector:target:argument:order:modes: */ -(void)internalInvoke:(NSArray *)data { AWEventHandler *handler = [data objectAtIndex:0]; id sender = [data objectAtIndex:1]; if(sender == [NSNull null]) sender = nil; id args = [data objectAtIndex:2]; if(args == [NSNull null]) args = nil; /* */ NSMethodSignature *mSig = [handler.target methodSignatureForSelector:handler.method]; if([mSig numberOfArguments] == 2) [handler.target performSelector:handler.method]; else if([mSig numberOfArguments] == 3) [handler.target performSelector:handler.method withObject:sender]; else if ([mSig numberOfArguments] == 4) [handler.target performSelector:handler.method withObject:sender withObject:args]; else @throw [NSException exceptionWithName:@"Invalid selector type" reason:@"This type of selector is not supported" userInfo:nil]; } -(void)dealloc { self.name = nil; [self clearReceivers]; [_handlers release]; [super dealloc]; } @end
#define DEFINE_EVENT(eventName) \ -(void)add ## eventName ## Handler:(id)receiver action:(SEL)action; \ -(void)remove ## eventName ## Handler:(id)receiver action:(SEL)action #define DEFINE_EVENT_IMPL(eventName, innerVariable) \ -(void)add ## eventName ## Handler:(id)receiver action:(SEL)action \ { \ [innerVariable addReceiver:receiver delegate:action]; \ } \ \ -(void)remove ## eventName ## Handler:(id)receiver action:(SEL)action \ { \ [innerVariable removeReceiver:receiver delegate:action] ; \ } \
AWEventHandlersList *_handlers;
DEFINE_EVENT(Event);
DEFINE_EVENT_IMPL(Event, _handlers)
addEventHandler:action:
and removeEventHandler:action:
and you can trigger an event through the invoke
methods of the _handlers
object. _handlers = [AWEventHandlersList new];
[_handlers release];
Source: https://habr.com/ru/post/168179/
All Articles