Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Multicast Delegation

Sponsored · Ship Features Fearlessly Turn features on and off without deploys. Used by thousands of Ruby developers.

Multicast Delegation

NSLondon January 2014 PechaKucha

Avatar for Luca Bernardi

Luca Bernardi

January 16, 2014
Tweet

More Decks by Luca Bernardi

Other Decks in Programming

Transcript

  1. @interface LBDelegateMatrioska () @property (nonatomic, strong) NSPointerArray *mutableDelegates; @end !

    ! @implementation LBDelegateMatrioska ! - (instancetype)initWithDelegates:(NSArray *)delegates { _mutableDelegates = [NSPointerArray weakObjectsPointerArray]; [delegates enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { [_mutableDelegates addPointer:(void *)obj]; }]; return self; }
  2. @interface LBRViewController () @property (weak, nonatomic) IBOutlet MKMapView *mapView; @property

    (strong, nonatomic) LBDelegateMatrioska *delegateMatrioska; @property (strong, nonatomic) LBClusteredMapViewDelegate *clusteringDelegate; @end ! @implementation LBRViewController ! - (void)viewDidLoad { [super viewDidLoad]; ! self.delegateMatrioska = ({ self.clusteringDelegate = [LBClusteredMapViewDelegate new]; NSArray *delegates = @[self.clusteringDelegate, self]; LBDelegateMatrioska *matrioska = [[LBDelegateMatrioska alloc] initWithDelegates:delegates]; matrioska; }); self.mapView.delegate = (id<MKMapViewDelegate>)self.delegateMatrioska; }
  3. @implementation LBDelegateMatrioska ! ! #pragma mark - NSObject ! -

    (BOOL)respondsToSelector:(SEL)aSelector { id firstResponder = [self p_firstResponderToSelector:aSelector]; return (firstResponder ? YES : NO); }
  4. @implementation LBDelegateMatrioska ! #pragma mark - NSProxy ! - (void)forwardInvocation:(NSInvocation

    *)invocation { for (id delegate in self.mutableDelegates) { if ([delegate respondsToSelector:invocation.selector]) { [invocation invokeWithTarget:delegate]; } } } ! - (NSMethodSignature *)methodSignatureForSelector:(SEL)sel { id firstResponder = [self p_firstResponderToSelector:sel]; if (firstResponder) { return [firstResponder methodSignatureForSelector:sel]; } return nil; }
  5. #pragma mark - NSProxy ! - (void)forwardInvocation:(NSInvocation *)invocation { //

    If the invoked method return void I can safely call all the delegates // otherwise I just invoke it on the first delegate that // respond to the given selector if ([invocation methodReturnTypeIsVoid]) { for (id delegate in self.mutableDelegates) { if ([delegate respondsToSelector:invocation.selector]) { [invocation invokeWithTarget:delegate]; } } } else { id firstResponder = [self p_firstResponderToSelector:invocation.selector]; [invocation invokeWithTarget:firstResponder]; } }
  6. context(@"when a method is called", ^{ it(@"should be received by

    all the delegates that receive the message", ^{ SEL testSelector = @selector(didSelect); #pragma clang diagnostic push #pragma clang diagnostic ignored "-Warc-performSelector-leaks" [[firstObject should] receive:testSelector]; [[secondObject should] receive:testSelector]; [[thirdObject shouldNot] receive:testSelector]; [(id <CannedProtocol>)matrioska performSelector:testSelector]; testSelector = @selector(didDeselect); [[firstObject shouldNot] receive:testSelector]; [[secondObject shouldNot] receive:testSelector]; [[thirdObject should] receive:testSelector]; [(id <CannedProtocol>)matrioska performSelector:testSelector]; #pragma clang diagnostic pop }); }); #import <Kiwi/NSProxy+KiwiVerifierAdditions.h>