[iOS 1 line code series] one line code bid farewell to delegate and block of complex view

Posted by alex57 on Sat, 04 Apr 2020 08:15:33 +0200

1. Preface

In development, when the custom view is complex and there are many events to be handled
You need to use delegate or block to pass events back and forth
When there are more levels, nesting also increases
The result is a delegate nested delegate or a block nested block
Upload layer by layer
I'm tired, I'm upset
When you need to modify or maintain it later
Jump here, jump there, jump for half a day to find a place

2. Main topic - UIResponder

Let's start with a diagram: UIResponder and its subclasses

UIResponder has a property: nextResponder, next responder
Use this to send events back and forth

New category:

/// UIResponder+JHRouter.h
@interface UIResponder (JHRouter)

- (void)jh_routerWithSelector:(NSString *)selector
                       sender:(id)sender
                         info:(NSDictionary *)info;

@end
/// UIResponder+JHRouter.m

#import "UIResponder+JHRouter.h"

@implementation UIResponder (JHRouter)

/*
 if an object respondsToSelector: selector
 [object respondsToSelector:NSSelectorFromString(selector)]
 you should do something.

 invoke [super jh_routerWithSelector:selector sender:sender info:info];
 Let the events continue to pass up
 */
- (void)jh_routerWithSelector:(NSString *)selector
                       sender:(id)sender
                         info:(NSDictionary *)info
{
    [[self nextResponder] jh_routerWithSelector:selector
                                         sender:sender
                                           info:info];
}

@end

When selector is used as SEL
You can add a classification to NSObject
Then directly

if ([self respondsToSelector:NSSelectorFromString(selector)]){
    [self performSelector:NSSelectorFromString(selector) withObjects:info];
}

/ / classification:

@interface NSObject (PerformSelector)

- (id)performSelector:(SEL)aSelector withObjects:(NSArray *)objects;

@end
#import "NSObject+PerformSelector.h"

@implementation NSObject (PerformSelector)

- (id)performSelector:(SEL)aSelector withObjects:(NSArray *)objects
{
    //
    NSMethodSignature *signature = [[self class] instanceMethodSignatureForSelector:aSelector];

    //
    if (!signature) {
        NSString *reason = [NSString stringWithFormat:@"oops~ unrecognized selector %@ sent to instance %@ : %lx",NSStringFromSelector(aSelector),[self class],(unsigned long)[self hash]];
        @throw [[NSException alloc] initWithName:@"com.haocold" reason:reason userInfo:nil];
        return nil;
    }

    //
    NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];
    invocation.target = self;
    invocation.selector = aSelector;

    // parameters
    // first: _cmd
    // second: target
    NSInteger arguments = signature.numberOfArguments - 2;

    //
    NSInteger count = MIN(arguments, objects.count);
    for (int i = 0; i < count; ++i) {
        id obj = objects[i];
        if ([obj isKindOfClass:[NSNull class]]) {
            obj = nil;
        }
        [invocation setArgument:&obj atIndex:i+2];
    }

    //
    [invocation invoke];

    //
    id result = nil;
    if (signature.methodReturnLength != 0) {
        [invocation getReturnValue:&result];
    }

    return result;
}

@end

selector can also be used as a flag, identifier

if ([selector isEqualToString:@"xxx"]) {
    // do something with info.
}

sender indicates the view of the trigger event. If you don't pay attention to this, you can send nil

info indicates the parameters to be passed. After each responder, some new parameters can be added

view1 added view2, view2 added view3

Within view3

[self.nextResponder jh_routerWithSelector:@"view3" sender:nil info:nil];

Within view2

- (void)jh_routerWithSelector:(NSString *)selector sender:(id)sender info:(NSDictionary *)info
{
    // Add some new parameters to info
    NSMutableDictionary *newInfo = [[NSMutableDictionary alloc] initWithDictionary:info];
    [newInfo setObject:@"name" forKey:@"xx"];
    [self.nextResponder jh_routerWithSelector:selector sender:sender info:newInfo];
}

Within view1

- (void)jh_routerWithSelector:(NSString *)selector sender:(id)sender info:(NSDictionary *)info{
    NSLog(@"info:%@",info);
    //info:{
    //  xx = name;
    //}
}

3. pay attention to

When adding a view to the window with a view, the echo event will be interrupted!!!