【デザインパターン】複数にデリゲートする方法 Multicast Delegate Pattern

Multicast Delegate Pattern

delegateは処理の一部を委譲するときに使うものです。
この通知先を複数にする方法を紹介します。

使用例

MultiDelegateProxy *delegateProxy = [[MultiDelegateProxy alloc] initWithTarget:self, nil];
[delegateProxy addTarget:xxx];
[delegateProxy addTarget:zzz];
self.scrollView.delegate = (id)delegateProxy;

実装例

#import <Foundation/Foundation.h>

@interface MultiDelegateProxy : NSProxy
- (instancetype)initWithTarget:(NSObject *)targets, ... NS_REQUIRES_NIL_TERMINATION;
- (void)addTarget:(NSObject *)target;
- (void)removeTarget:(NSObject *)target;
@end
#import "MultiDelegateProxy.h"

@interface MultiDelegateProxy ()
@property (strong, nonatomic) NSMutableArray* targets;
@end

@implementation MultiDelegateProxy

- (instancetype)initWithTarget:(NSObject *)targets, ... {
    _targets = @[].mutableCopy;
    va_list arguments;
    va_start(arguments, targets);
    NSObject *value = targets;
    while (value) {
        [_targets addObject:value];
        value = va_arg(arguments, typeof(NSObject*));
    }
    va_end(arguments);
    return _targets.count ? self : nil;
}

- (void)forwardInvocation:(NSInvocation *)invocation {
    for(NSObject *target in _targets.copy) {
        if ([target respondsToSelector:invocation.selector]) {
            [invocation invokeWithTarget:target];
        }
    }
}

- (NSMethodSignature *)methodSignatureForSelector:(SEL)sel {
    for(NSObject *target in _targets.copy) {
        if ([target respondsToSelector:sel]) {
            return [target methodSignatureForSelector:sel];
        }
    }
    return nil;
}

- (BOOL)respondsToSelector:(SEL)aSelector {

    if ([super respondsToSelector:aSelector]) {
        return YES;
    }

    for(NSObject *target in _targets.copy) {
        if ([target respondsToSelector:aSelector]) {
            return YES;
        }
    }
    return NO;
}

- (BOOL)conformsToProtocol:(Protocol *)aProtocol {
    for(NSObject *target in _targets.copy) {
        if ([target conformsToProtocol:aProtocol]) {
            return YES;
        }
    }
    return NO;
}

- (void)addTarget:(NSObject *)target {
    if (target && ![_targets containsObject:target]) {
        [_targets addObject:target];
    }
}

- (void)removeTarget:(NSObject *)target {
    if (target && [_targets containsObject:target]) {
        [_targets removeObject:target];
    }
}

@end

参考リンク

Article

http://blog.fenrir-inc.com/jp/2013/11/nsproxy.html
http://blog.scottlogic.com/2012/11/19/a-multicast-delegate-pattern-for-ios-controls.html
http://www.gregread.com/2016/02/23/multicast-delegates-in-swift/

OSS

https://github.com/tumtumtum/SwiftMulticastDelegate
https://github.com/jonasman/MulticastDelegate