Domino 1.0.3

Domino 1.0.3

李云鹏 维护。



Domino 1.0.3

  • 作者:
  • 李云鹏

Domino

中文版

Domino 是 Objective-C 中多级对象之间的通信机制。

为什么使用 Domino?

当实现复杂 UI 时,通常会形成多级关系,如下所示

relationships

随着 ContentViewController 商务的发展,ContentViewController/ContainerViewController/RootViewController 的代理将越来越大。ContainerViewController/RootViewController 可能不会关心那么多事件,但仍需要将这些事件传给上级。Domino就是为了解决这个问题的。

Domino 在对象之间创建一个类似响应链的事件链。

适用场景
  • 父 VC 包含子 VC。子 VC 和其他子 VC 依次包含。
  • 还有一些事件由与 VC 分离的某些管理者标记,需要将这些事件传给 VC。
  • 需要知道更多对象中的委托(如 UIScrollDelegate)中的事件。
与代理相比
  • 避免实现转发了消息的方法。
  • 委托只能进行一对一通信。但某些事件需要通知多个对象。
  • 忽略命名空间。(也就是说,事件可以直接跨组件传递)
与 Notification 的比较
  • 当有多个实例时,Notification 不是一个好的选择。

  • 只有上级才能接收到事件,这意味着管理比 notification 更严格。

     

Domino 入门

系统响应链中存在默认事件链。如果所有相关对象都在同一个响应链上,您就不需要做任何配置。

如果不属于同一个响应链,您可以按照以下方式将对象挂载到链中

[dddManager.domino mountAtDomino:contentVC.domino]

您也可以通过使用 unmountFromPredomino: 从前一个节点卸载。链不会引起保留循环,因此不需要在对象销毁时调用 unmountFromPredomino

Domino 事件包含 SimpleEventSelectorEvent。**事件仅从下到上传递,无论何种事件**。

SimpleEvent

使用 NSString 作为事件名,使用 NSDictionary 存储参数,就像 NSNotification 一样

SelectorEvent

更类似于委托。SelectorEvent 由协议定义。每个方法都是一种 SelectorEvent。通过这种方式调用方法和传递参数更简单、更正式。因此,在大多数情况下,建议使用 SelectorEvent。

对于返回值的 selector 事件,一旦有任何对象处理了事件,事件就不会传递到下一个节点。其机制与责任链相同。

对于不返回任何值的 selector 事件,事件会像通知一样通知所有合法的订阅者。

首先,您最好创建一个新文件来声明事件。

// ContentViewControllerEvents.h

/// SimpleEvent
extern NSString * const ContentViewControllerStatisticsEvent;

/// SelectorEvent
@protocol ContentViewControllerEvents <NSObject>
@optional
- (void)contentDidLoadWithArg1:(NSString *)arg1 arg2:(NSInteger)arg2;
- (void)contentDidLoadWithArg:(NSInteger)arg;
- (NSString *)fetchChannelId;
@end

发布事件

#import "Domino.h"
#import "ContentViewControllerEvents.h"

@DominoSelectorEvents(ContentViewControllerEvents); // declare events would be posted
@interface ContentViewController ()

@end

@implementation ContentViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // ...
    
    // post SelectorEvent
  	[self.domino.trigger contentDidLoadWithArg:12];
  
    // post NormalEvent
    [self.domino.trigger postEvent:ContentViewControllerStatisticsEvent params:@{@"msg":@"did load"}];
  	
}
@end

订阅事件

#import "Domino.h"
#import "ContentViewControllerEvents.h"

@interface ContainerViewController () // <ContentViewControllerEvents> NOT neccessary
@end
@implementation ContainerViewController
- (void)viewDidLoad {
    [super viewDidLoad];
    [self.domino.tracker subscribeSelectorEvent:@selector(contentDidLoadWithArg:) target:self];
  
    [self.domino.tracker subscribeEvent:ContentViewControllerStatisticsEvent handler:^(NSDictionary *params) {
        NSLog(@"[%@]%@",ContentViewControllerStatisticsEvent, params);
    }];
}

- (void)contentDidLoadWithArg:(NSInteger)arg {
    NSLog(@"ContainerViewController - contentDidLoad %td",arg);
}
@end

重构事件参数

@interface ContainerViewController ()<DominoInterceptor> // !!! declare IS neccessary !!!
@end
@implementation ContainerViewController
- (void)reformDominoParams:(DominoSelectorEventParams *)params forSelectorEvent:(SEL)selector {
    if (selector == @selector(contentDidLoadWithArg1:arg2:)) {
        NSLog(@"ContainerViewController - reform @selector(contentDidLoadWithArg1:arg2:)");
        params[0] = @"hook!!!"; // index from 0
        params[1] = @(333); // Don't worry about type
    }
}
@end

当您需要拦截某些事件链时,您可以使用 BOOL DominoProtocolContainSelector(Protocol *protocol, SEL selector); 来确定一个选择器是否属于该协议。这允许在必要时拦截所有非公开事件。

线程

所有线程都安全,除了 DominoSelectorEventParams。由于大多数场景是UI事件,因此默认情况下,所有订阅者都会在主线程上被调用。

当然,还有两种其他模式。

typedef NS_ENUM(NSInteger, DominoTriggerMode) {
    DominoTriggerModeMainThread,  // default
    DominoTriggerModeBackground,
    DominoTriggerModeCurrentThread,
};

您可以通过 [Domino setTriggerMode:DominoTriggerModeBackground] 来更改模式。模式不影响具有返回值的选择器事件,并且此类事件的订阅者始终在发布此事件的用户所在的线程上调用。

安装

如果您使用 Cocoapods,请在 Podfile 中添加 pod 'Domino', '~> 1.0.1'

您还可以将两个文件 Domino.h/m 添加到项目中。没有其他要求。

许可

Domino 采用 MIT 许可发布。有关详细信息,请参阅 LICENSE