• BlocksKit初见:一个支持将delegate转换成block的Cocoa库


    简单介绍

    BlocksKit 是一个开源的框架,对 Cocoa 进行了扩展。将很多须要通过 delegate 调用的方法转换成了 block。在非常多情况下。blocks 比 delegate 要方便简单。由于 block 是紧凑的,能够使代码简洁。提高代码可读性。另外 block 还能够进行异步处理。使用 block 要注意避免循环引用。

    文件夹结构

    BlocksKit 的全部方法都以bk_开头,这样能够方便地列出全部 BlocksKit 的全部方法。BlocksKit 主要文件夹结构

    • Core:存放 Foundation 相关的 Block category,如 NSObject、NSTimer、NSarray、NSDictionary、NSSet、NSIndexSet、NSMutableArray等
    • DynamicDelegate:动态代理(消息转发机制)
    • UIKit:扩展了 UIAlertView。UIActionView,UIButton 等

    最经常使用的是 UIKit Category。它为 UIAlertView,UIActionSheet,UIButton,UITapGestureRecognizer 等提供了 blocks。

    使用方法实例

    UIAlertView 和 UIActionSheet 使用方法演示样例:

    UIAlertView *alertView = [[UIAlertView alloc] bk_initWithTitle:@"提示" message:@"提示信息"];
    [alertView bk_setCancelButtonWithTitle:@"取消" handler:nil];
    [alertView bk_addButtonWithTitle:@"确定" handler:nil];
    [alertView bk_setDidDismissBlock:^(UIAlertView *alert, NSInteger index) {
        if (index == 1) {
            NSLog(@"%ld clicked",index);
        }
    }];
    [alertView show];
    [[UIActionSheet bk_actionSheetCustomWithTitle:nil buttonTitles:@[@"查看", @"退出"] destructiveTitle:nil cancelTitle:@"取消" andDidDismissBlock:^(UIActionSheet *sheet, NSInteger index) {
    
    }] showInView:self.view];
    

    UIButton 和 UITapGestureRecognizer 使用方法演示样例:

    UIButton *button = [[UIButton alloc] init];
    [button bk_addEventHandler:^(id sender) {
    
    } forControlEvents:UIControlEventTouchUpInside];
    
    UITapGestureRecognizer *tapGestureRecognizer = [UITapGestureRecognizer bk_recognizerWithHandler:^(UIGestureRecognizer *sender, UIGestureRecognizerState state, CGPoint location) {
        if (state == UIGestureRecognizerStateRecognized) {
            ...
        }
    }];

    UIButton 和 UIGesture 将 target-action 转换成 block,实现较简单:

    - (id)bk_initWithHandler:(void (^)(UIGestureRecognizer *sender, UIGestureRecognizerState state, CGPoint location))block delay:(NSTimeInterval)delay
    {
        self = [self initWithTarget:self action:@selector(bk_handleAction:)];
        if (!self) return nil;
    
        self.bk_handler = block;
        self.bk_handlerDelay = delay;
    
        return self;
    }
    
    - (void)bk_handleAction:(UIGestureRecognizer *)recognizer
    {
        void (^handler)(UIGestureRecognizer *sender, UIGestureRecognizerState state, CGPoint location) = recognizer.bk_handler;
        if (!handler) return;
    
        ...
    
        if (!delay) {
            block();
            return;
        }
    
        ...
    }

    delegate 转换成 block 实际上使用了消息转发机制,是 BlocksKit 源代码中最难理解的部分。

    原理分析: 消息转发机制

    当一个对象收到它没实现的消息的时候。一般会发生例如以下的情况。

    1. 调用+(BOOL)resolveInstanceMethod:(SEL)aSEL,假设对象在这里动态加入了selector 的实现方法,则消息转发结束,否则运行步骤2
    2. 调用 - (id)forwardingTargetForSelector:(SEL)aSelector,在这里你能够将消息转发给其它对象。假设实现则消息转发结束,否则运行步骤3
    3. 运行完整的消息转发机制,调用-(void)forwardInvocation:(NSInvocation *)invocation 在这一步,你能够改动消息的不论什么内容。包含目标(target),selector,參数。

      假设没有实如今这里还未实现转发则程序将抛出异常。

    原理实例分析

    BlocksKit 动态代理实现方式是最后一步,即-(void)forwardInvocation:(NSInvocation *)invocation,使得动态代理能够接受随意消息。

    以UIAlertView为例。UIAlertView在运行时动态关联了A2DynamicUIAlertViewDelegate

    @implementation UIAlertView (BlocksKit)
    
    @dynamic bk_willShowBlock, bk_didShowBlock, bk_willDismissBlock, bk_didDismissBlock, bk_shouldEnableFirstOtherButtonBlock;
    
    + (void)load
    {
        @autoreleasepool {
            [self bk_registerDynamicDelegate];
            [self bk_linkDelegateMethods:@{
                @"bk_willShowBlock": @"willPresentAlertView:",
                @"bk_didShowBlock": @"didPresentAlertView:",
                @"bk_willDismissBlock": @"alertView:willDismissWithButtonIndex:",
                @"bk_didDismissBlock": @"alertView:didDismissWithButtonIndex:",
                @"bk_shouldEnableFirstOtherButtonBlock": @"alertViewShouldEnableFirstOtherButton:"
            }];
        }
    }

    A2DynamicUIAlertViewDelegate 是 A2DynamicDelegate 的子类。并实现了UIAlertViewDelegate 的方法

    代理消息的转发由 A2DynamicDelegate 完毕

    - (void)forwardInvocation:(NSInvocation *)outerInv
    {
        SEL selector = outerInv.selector;
        A2BlockInvocation *innerInv = nil;
        if ((innerInv = [self.invocationsBySelectors bk_objectForSelector:selector])) {
            [innerInv invokeWithInvocation:outerInv];
        } else if ([self.realDelegate respondsToSelector:selector]) {
            [outerInv invokeWithTarget:self.realDelegate];
        }
    }

    注: 文章由我们 iOS122(http://www.ios122.com)的小伙伴 @鱼 整理,喜欢就一起參与: iOS122 任务池

  • 相关阅读:
    [CSP-S模拟测试]:Merchant(二分答案)
    [CSP-S模拟测试]:回文(hash+二维前缀和)
    [CSP-S模拟测试]:排列组合(数学 or 找规律)
    [CSP-S模拟测试]:X国的军队(贪心)
    BZOJ3714 [PA2014]Kuglarz 【最小生成树】
    BZOJ3922 Karin的弹幕 【线段树】
    BZOJ3495 PA2010 Riddle 【2-sat】
    BZOJ2597 [Wc2007]剪刀石头布 【费用流】
    hdu6184 Counting Stars 【三元环计数】
    BZOJ4815 [CQOI2017]小Q的表格 【数论 + 分块】
  • 原文地址:https://www.cnblogs.com/liguangsunls/p/7357223.html
Copyright © 2020-2023  润新知