美文网首页iOS基本功
ios 将代理方法组件化(消息转发,多代理)

ios 将代理方法组件化(消息转发,多代理)

作者: Ricky666 | 来源:发表于2019-02-28 14:51 被阅读49次

在工作的过程中,我们经常会遇到一些需求是这样的:限制textField的输入字数上限,且只允许输入数字;反复使用同样代码的delegate方法;以及delegate方法实现的内容相同但是代码不统一,等等。这样很影响工作的效率,因为只能凭着自己写过的记忆去寻找某个delegate的实现方法。于是我就开始思考如何整理常用的delegate方法的实现。
用一个常用的具体例子来思考处理的办法,比如:UITextField需要限制输入字符的长度,且需要不能输入emoji表情。

我的第一个想法是将UITextField再进行一次的封装,将限制长度的方法(Method1)和限制输入字符的方法(Method2)都封装到textField的子类里。
这个时候又想到了可能Method1可能在某些业务场景下,要放在Method2之后。那没问题,可以再textField的子类中再加一个属性控制。然后继续思考,可能delegate方法需要在业务层里面有入口,那就继续在子类中再加上will和end两个回调。

到了这里,我感觉就略显尴尬了,因为有些时候Method1和Method2之间,也可能业务层需要有操作,如果继续添加回调就会太过于臃肿,并且如果后面还有Method3,4,5,那么这个子类可能会越来越难以维护。
所以,回到问题的原点,可不可以将需要组件化的method一个一个的封装起来呢?但是这样做的话,由于OC只能单delegate的问题,我们还需要实现一个可以多重代理的center,去转发消息给消息接收者handler,然后handler再自己处理需要实现的method。这样可以很好的完成我们的目标,且降低代码耦合度。


直接上center的代码
center.h

@interface RCYMultableDelegateTransmitCenter : NSObject

- (void)addTransmitDelegates:(NSArray<id> *)delegates;

- (void)insertTransmitDelegates:(NSArray<id> *)delegates index:(NSUInteger)index;

- (void)updateTransmitDelegates;

- (void)removeTransitDelegateAtIndex:(NSInteger)index;

- (void)removeAllTransmitDelegates;

- (NSArray<id> *)AllDelegates;

@end

center.m

#import "RCYMultableDelegateTransmitCenter.h"

@interface RCYMultableDelegateTransmitCenter ()

@property (nonatomic, strong) NSPointerArray *delegatePointerArray;

@end

@implementation RCYMultableDelegateTransmitCenter

#pragma mark - publish
- (void)addTransmitDelegates:(NSArray<id> *)delegates {
    for (id delegate in delegates) {
        [self.delegatePointerArray addPointer:(__bridge void *)delegate];
    }
}

- (void)insertTransmitDelegates:(NSArray<id> *)delegates index:(NSUInteger)index {
    if (index > self.delegatePointerArray.count - 1 || !self.delegatePointerArray.count) {
        [self addTransmitDelegates:delegates];
        return;
    }
    for (int i = 0; i < delegates.count; i++) {
        [self.delegatePointerArray insertPointer:(__bridge void *)(delegates[i]) atIndex:index + i];
    }
}

- (void)updateTransmitDelegates {
    [self.delegatePointerArray addPointer:NULL];
    [self.delegatePointerArray compact];
}

- (void)removeTransitDelegateAtIndex:(NSInteger)index {
    if (self.delegatePointerArray.count > index) {
        [self.delegatePointerArray removePointerAtIndex:index];
        [self updateTransmitDelegates];
    }
}

- (void)removeAllTransmitDelegates {
    self.delegatePointerArray = [NSPointerArray weakObjectsPointerArray];
}

- (NSArray<id> *)AllDelegates {
    return self.delegatePointerArray.allObjects;
}

#pragma mark - private
- (BOOL)respondsToSelector:(SEL)aSelector{
    if ([super respondsToSelector:aSelector]) {
        return YES;
    }
    for (id target in self.delegatePointerArray) {
        if ([target respondsToSelector:aSelector]) {
            return YES;
        }
    }
    
    return NO;
}

- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
    NSMethodSignature *sig = [super methodSignatureForSelector:aSelector];
    if (!sig) {
        for (id target in self.delegatePointerArray) {
            if ((sig = [target methodSignatureForSelector:aSelector])) {
                break;
            }
        }
    }
    
    return sig;
}

- (void)forwardInvocation:(NSInvocation *)anInvocation{
    for (id target in self.delegatePointerArray) {
        if ([target respondsToSelector:anInvocation.selector]) {
            [anInvocation invokeWithTarget:target];
        }
    }
}

#pragma mark - getter
- (NSPointerArray *)delegatePointerArray {
    if (!_delegatePointerArray) {
        _delegatePointerArray = [NSPointerArray weakObjectsPointerArray];
    }
    return _delegatePointerArray;
}

可以看到,center通过消息转发,实现了将delegate转发给NSPointerArray里面的对象,于是我们可以通过创建实现<UITextFieldDelegate>的不同Object,来处理不同的业务需要。

这里贴一个限制textView输入的代码,具体Demo请看这里,目前只实现了textField和textView的限制输入方法。

@interface ViewController ()<UITextViewDelegate>

@property (nonatomic, strong) UITextView *textView;

@property (nonatomic, strong) RCYTextViewLimitLengthHandler *textViewLimitLengthHandler;
@property (nonatomic, strong) RCYTextViewLimitCharacterHandler *textViewLimitCharHandler;

@property (nonatomic, strong) RCYMultableDelegateTransmitCenter *textViewCenter;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    self.textView = [[UITextView alloc] init];
    self.textView.frame = CGRectMake(0, 30, 200, 50);
    self.textView.backgroundColor = UIColor.redColor;
    
    [self.view addSubview:self.textView];
    
    self.textViewLimitLengthHandler = [[RCYTextViewLimitLengthHandler alloc] init];
    self.textViewLimitLengthHandler.maxLimit = 5;
    self.textViewLimitLengthHandler.maxLimitBlock = ^{
        NSLog(@"已经输入了5个字了");
    };
    
    self.textViewLimitCharHandler = [[RCYTextViewLimitCharacterHandler alloc] init];
    self.textViewLimitCharHandler.state = RCYLimitCharacterStateChinese | RCYLimitCharacterStateEmoji;//禁止输入汉字/表情
    self.textViewLimitCharHandler.limitBlock = ^{
        NSLog(@"view输入不可描述内容");
    };
    
    self.textViewCenter = [[RCYMultableDelegateTransmitCenter alloc] init];
    //这里,业务层的self也可以作为handler传给center
    [self.textViewCenter addTransmitDelegates:@[self.textViewLimitLengthHandler, self, self.textViewLimitCharHandler]];
    
    self.textView.delegate = (id<UITextViewDelegate>)self.textViewCenter;
    
    UIButton *removeLimitButton = [[UIButton alloc] init];
    [removeLimitButton setTitle:@"取消限制" forState:UIControlStateNormal];
    [removeLimitButton setTitleColor:UIColor.redColor forState:UIControlStateNormal];
    removeLimitButton.frame = CGRectMake(200, 30, 120, 50);
    [removeLimitButton addTarget:self action:@selector(removeAllLimit) forControlEvents:UIControlEventTouchUpInside];
    [self.view addSubview:removeLimitButton];
    
    UIButton *addLimitButton = [[UIButton alloc] init];
    [addLimitButton setTitle:@"添加限制" forState:UIControlStateNormal];
    [addLimitButton setTitleColor:UIColor.redColor forState:UIControlStateNormal];
    addLimitButton.frame = CGRectMake(200, 100, 120, 50);
    [addLimitButton addTarget:self action:@selector(addAllLimit) forControlEvents:UIControlEventTouchUpInside];
    [self.view addSubview:addLimitButton];
}

- (void)removeAllLimit {
    //两种方式都可以
    //    self.textViewLimitLengthHandler = nil;
    //    [self.textViewCenter updateTransmitDelegates];
    [self.textViewCenter removeTransitDelegateAtIndex:0];
    NSLog(@"移除了限制");
}

- (void)addAllLimit {
    [self.textViewCenter addTransmitDelegates:@[self.textViewLimitLengthHandler]];
    NSLog(@"添加了限制");
}

- (void)textViewDidChange:(UITextView *)textView {
    NSLog(@"改变了");
}

@end

相关文章

网友评论

    本文标题:ios 将代理方法组件化(消息转发,多代理)

    本文链接:https://www.haomeiwen.com/subject/afwcuqtx.html