iOS: 基於NSInvocation的事件總線 (支持同步返回結果)

1.前言:

如今模塊間不少用事件總線解耦。以往的事件總線都是單向傳遞數據, 返回數據只能異步回調; 有時我想發送一個數據到某模塊處理完返回結果才進行下一步驟, 若是一個地方出現屢次請求數據,異步嵌套代碼可讀性很低; 想用 'performSelector' 來解耦函數調用,但有參數限制, 而後發現NSInvocation更增強大,因此選用NSInvocation來實現事件總線。git

區別 NSInvocation performSelector NSNotification
參數 可多參數 限制參數數量 用userInfo傳入參數
返回結果 有, 同步 有, 同步 單向傳遞, 無
調用耗時 0.006ms 0.005ms
返回結果耗時 0.007ms 0.003ms

2.用法

  1. 支持多參數傳遞,同步返回結果,耗時與直接調用該方法相差無幾
  2. 支持訂閱對象方法和類方法
  3. 無需手動註銷訂閱

1.定義事件:

// 在'DVEventBusDefine.h' 或者 'PCH'文件 自定義我的喜愛事件格式
#define kEVENT(event) static NSString *const kEVENT_##event = @"kEVENT_"#event;

#define kEVENT_MODULE(module,event) kEVENT(module##_##event)
#define kEVENT_GLOBAL_MODULE(module,event) kEVENT(GLOBAL_##module##_##event)
#define kEVENT_SERVICE_MODULE(module,event) kEVENT(SERVICE_##module##_##event)
#define kEVENT_DAO_MODULE(module,event) kEVENT(DAO_##module##_##event)
#define kEVENT_VM_MODULE(module,event) kEVENT(VM_##module##_##event)


// 建立 'DemoEvent.h' 自定義事件
kEVENT(DEMO)                            // 等於 kEVENT_DEMO
kEVENT_MODULE(DEMO, GET_DATA)           // 等於 kEVENT_DEMO_GET_DATA
kEVENT_SERVICE_MODULE(DEMO, GET_DATA)   // 等於 kEVENT_SERVICE_DEMO_GET_DATA
kEVENT_DAO_MODULE(DEMO, GET_DATA)       // 等於 kEVENT_DAO_DEMO_GET_DATA

複製代碼

2. 訂閱和發佈 類事件

2.1 訂閱類事件 : 類事件是綁定類方法
@interface DemoService() <DVEventBusClassDelegate> // 繼承 'DVEventBusClassDelegate'

@end


@implementation DemoService

/**
    類事件 綁定 類方法
    實現 'DVEventBusClassDelegate''event_classMethod_map' 方法
*/
+ (NSDictionary<NSString *,NSString *> *)event_classMethod_map {
    return @{
        kEVENT_SERVICE_DEMO_ADD          : _sel(@selector(addWithA:b:)),
        kEVENT_SERVICE_DEMO_GET_STRING   : _sel(@selector(getString)),
        kEVENT_SERVICE_DEMO_GET_OBJECT   : _sel(@selector(getObject)),
        kEVENT_SERVICE_DEMO_LOGIN        : _sel(@selector(loginWithUserName:password:success:)),
        kEVENT_SERVICE_DEMO_MUTIL_METHOD : _sel(@selector(mutilMethod1)) // 一個事件綁定多個方法, 順序執行
                                          ._sel(@selector(mutilMethod2))
                                          ._sel(@selector(mutilMethod3))
    };
}


/*
    傳入數字類型必須是'NSNumber'類型,返回數字類型也是'NSNumber'類型
*/
+ (NSNumber *)addWithA:(NSNumber *)a b:(NSNumber *)b {
    int ret = [a intValue] + [b intValue];
    return @(ret);
}

+ (NSString *)getString {
    return @"Hello world";
}

+ (UserModel *)getObject {
    UserModel *model = [[UserModel alloc] init];
    model.userName = @"David";
    model.password = @"123456";
    return model;
}

+ (void)loginWithUserName:(NSString *)userName password:(NSString *)password success:(void(^)(BOOL status))success {
    NSLog(@"[DemoService LOG]: 登陸成功, userName-> %@, password-> %@", userName, password);
    success(YES);
}

+ (void)mutilMethod1 {
    NSLog(@"[DemoService LOG]: 觸發方法1");
}

+ (void)mutilMethod2 {
    NSLog(@"[DemoService LOG]: 觸發方法2");
}

+ (void)mutilMethod3 {
    NSLog(@"[DemoService LOG]: 觸發方法3");
}

@end
複製代碼
2.2.1 註冊類事件:
// 用這個方法註冊, 類必須繼承'DVEventBusClassDelegate' 和 實現'event_classMethod_map'
[DVEventSubscriber registerClassEvents:[DemoService class]];
複製代碼
2.2.2 第二種訂閱類事件方法:
也能夠用下面方法訂閱類事件,類不用繼承'DVEventBusClassDelegate', 若是事件多仍是建議使用上面方法
[DVEventSubscriber addClasslEvent:kEVENT_SERVICE_DEMO_LOGIN subscriber:[DemoService class] action:@selector(loginWithUserName:password:success:)];
[DVEventSubscriber addClasslEvent:kEVENT_SERVICE_DEMO_ADD subscriber:[DemoService class] action:@selector(addWithA:b:)];
[DVEventSubscriber addClasslEvent:kEVENT_SERVICE_DEMO_GET_STRING subscriber:[DemoService class] action:@selector(getString)];
[DVEventSubscriber addClasslEvent:kEVENT_SERVICE_DEMO_GET_OBJECT subscriber:[DemoService class] action:@selector(getObject)];
[DVEventSubscriber addClasslEvent:kEVENT_SERVICE_DEMO_MUTIL_METHOD subscriber:[DemoService class] action:@selector(mutilMethod1)];
[DVEventSubscriber addClasslEvent:kEVENT_SERVICE_DEMO_MUTIL_METHOD subscriber:[DemoService class] action:@selector(mutilMethod2)];
[DVEventSubscriber addClasslEvent:kEVENT_SERVICE_DEMO_MUTIL_METHOD subscriber:[DemoService class] action:@selector(mutilMethod3)];
複製代碼
2.3 發佈類事件 :發佈類事件是執行類方法
/*
    發送事件能夠同步返回結果, 跟直接調用方法耗時差很少
    多參數傳入, 結尾必須加 nil
*/
NSNumber *count = [DVEventPublisher publishClassEvent:kEVENT_SERVICE_DEMO_ADD params:@(1), @(2), nil];

NSString *string = [DVEventPublisher publishClassEvent:kEVENT_SERVICE_DEMO_GET_STRING];

UserModel *user = [DVEventPublisher publishClassEvent:kEVENT_SERVICE_DEMO_GET_OBJECT];


// 發送事件異步回調結果
void(^successBlock)(BOOL) = ^(BOOL status) {
    // 處理程序    
};
[DVEventPublisher publishClassEvent:kEVENT_SERVICE_DEMO_LOGIN params:@"Hello", @"654321", successBlock, nil];


// 發佈一個類事件能夠順序執行多個類方法
[DVEventPublisher publishClassEvent:kEVENT_SERVICE_DEMO_MUTIL_METHOD];
複製代碼

3. 訂閱和發佈 對象事件 (跟類事件使用方法差很少)

3.1 訂閱對象事件 : 事件綁定已實例化後的對象方法
@interface Demo() <DVEventBusDelegate> // 繼承 'DVEventBusDelegate'

@end


@implementation Demo

/**
    事件 綁定 對象方法
    實現 'DVEventBusDelegate''event_method_map' 方法
*/
- (NSDictionary<NSString *,NSString *> *)event_method_map {
    return @{
        kEVENT_DEMO_ADD : _sel(@selector(addWithA:b:)),
    };
}


- (NSNumber *)addWithA:(NSNumber *)a b:(NSNumber *)b {
    int ret = [a intValue] + [b intValue];
    return @(ret);
}

@end
複製代碼
3.2.1 註冊對象事件
// 用這個方法註冊, 對象必須繼承'DVEventBusDelegate' 和 實現'event_method_map'
[DVEventSubscriber registerEvents:object];
複製代碼
3.2.2 第二種訂閱對象事件方法:
也能夠用下面方法訂閱對象事件,對象不用繼承'DVEventBusDelegate', 若是事件多仍是建議使用上面方法
[DVEventSubscriber addEvent:kEVENT_DEMO_ADD subscriber:object action:@selector(addWithA:b:)];
複製代碼
3.3 發佈對象事件 :發佈對象事件是執行對象方法
// 同步返回結果, 如有參數, 結尾要nil
NSNumber *count = [DVEventPublisher publishEvent:kEVENT_DEMO_ADD params:@(1), @(2), nil];
複製代碼

3.如何導入項目

  1. 編譯DVEventBusKitShell

  1. 生成Framework拖入項目

  1. 項目 Target -> Build Settings -> Linking ->Other Linker Flags 添加參數: -all_load -ObjC github

  2. 在PCH文件導入bash

#import <DVEventBusKit/DVEventBusKit.h>
複製代碼

4.結語:

github地址: github.com/shidavid/DV…
謝謝你們觀看,有興趣麻煩點個星星關注下 😁😁😁異步

相關文章
相關標籤/搜索