一个对RxSwift集成思想的借鉴:KKReactors

博客介绍了KKReactors类库,该库用于封装KVO和Notification,旨在简化iOS开发中的观察者模式。通过KKReactors,可以更方便地设置观察者,避免手动管理KVO的复杂性,并提供了过滤器功能来控制回调频率。此外,KKReactors还支持键盘通知和自定义消息订阅,通过Block实现响应式编程,提高代码可读性和灵活性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

使用KVO遇到的痛点

作为一个iOS开发老司机,我在日常使用KVO过程中发现了KVO的一些不足:
1、调用复杂,算上调用者,总共涉及到5个参数
2、响应函数与调用地方分隔在不同的地方,阅读代码需要跳转
3、一个观察者观察了多个对象后,回调里不好区分究竟是哪个变量发生了回调
4、需要手动释放KVO,过多的调用removeObserver会导致闪退
5、对于极其频繁的变量变动,我们无法屏蔽一些过于密集的KVO调用

解决思路

鉴于KVO以上的种种缺点,我决定封装一个类,这个类似单例,叫做KKReactors,从而来简化KVO的使用:
下面罗列了以上5个痛点的解决思路:
1、封装接口,屏蔽无用的content参数,写死option为NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld,只留下最少的参数
2、不要使用target-action模式,借鉴Rxswift思想,使用响应式编程,让Block来响应
3、KKReactors内部已经实现了对回调的区分,调用者只需要响应Block即可,一个block只响应一个KVO
4、告诉KKReactors,我们的观察工作将会伴随着companion的释放而自动结束,无需手动释放KVO
5、我们可以给KKReactors安装过滤器,过滤器提供了上次变动的时间戳,上次调用的时间戳,用面向函数编程的思想,让调用者自己编程实现对响应的过滤

KKReactors承接了所有KVO的回调,这种回调还是以target-action方式进行的,然后KKReactors根据content参数来区分具体是哪个KVO活动,然后KKReactors再对相应的KVO活动进行block方式的回调。
回调时如果发现companion参数已经为空,就不再进行block方式的回调,而是对KVO活动进行销毁

技术亮点

1、借鉴了Rxswift里disposeBag的思想,disposeBag回收的时候自动释放资源。我们的KVO也会随着companion的释放而自动释放
2、使用了函数式编程。对时间的过滤不是采用过滤的选项,而是开放了过滤函数及充足的参数,让用户之地你故意过滤规则,更自由、更灵活
3、返回值是KKWatching类型的,它的本质是: id,它是一种协议,而不是简单的对象,协议里提供了释放对方的两种方法,但不提供对象内部实现细节,内部尽可能少地对外暴露实现细节,体现了六大设计原则里的依赖倒置原则,又充分体现了面向协议编程的思想
4、用Block的方式,返回2个值,解决了OC不支持返回多个返回值的缺陷

代码

头文件

//
//  KKReactors.h
//  KKReactors
//
//  Created by 龚成诗 on 2020/3/22.
//

#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>

NS_ASSUME_NONNULL_BEGIN

#pragma mark - KKBaseReaction declaration
@protocol KKBaseReaction <NSObject>//基础协议

@optional

//立刻销毁这个reactor活动
- (void)disposeNow;
///设置本reactor与companion同寿,内部对companion是弱引用,companion被置空后,这个reactor自动销毁
- (void)persistWithCompanion:(NSObject*)companion;

@end

#pragma mark - KKWatchReaction declaration
//KVO(watch)接口返回值类型,返回值能提供给外部调用的接口都在协议里,含基类协议
@protocol KKWatchReaction <KKBaseReaction>

@optional

///给KVO的回调加装过滤器,传空表示移除过滤器
- (void)installFilter:(BOOL (^ __nullable)(NSInteger counter,//返回YES表示不过滤(需要执行),返回NO表示抛弃这次回调
                                           NSTimeInterval responserInterval,//上次对responser的调用距离现在多久
                                           NSTimeInterval variationInterval,//上次值变动距离现在多久
                                           NSObject* target,//观察对象
                                           NSString* keyPath,//观察变量、路径
                                           NSObject* __nullable oldValue,
                                           NSObject* __nullable newValue))filter;

@end
///用KKWatching简化KVO(watch)接口返回值类型
#define KKWatching id<KKWatchReaction>


//notification(listen)接口返回值,返回值能提供给外部调用的接口都在协议里,含基类协议
@protocol KKListenReaction <KKBaseReaction>

@optional
///给Notification的回调加装过滤器,传空表示移除过滤器
- (void)installFilter:(BOOL (^ __nullable)(NSInteger counter,//返回YES表示不过滤,需要执行,返回NO表示抛弃这次回调
                                           NSTimeInterval responserInterval,//上次对responser的调用距离现在多久
                                           NSTimeInterval notificationInterval,//上次发通知距离现在多久
                                           NSString* name))filter;

@end
///用KKListening简化notification(listen)接口返回值类型
#define KKListening id<KKListenReaction>


//消息订阅(subscribe)接口返回值,返回值能提供给外部调用的接口都在协议里,含基类协议
@protocol KKSubscribeReaction <KKBaseReaction>

@optional
///给消息订阅的回调加装过滤器,传空表示移除过滤器
- (void)installFilter:(BOOL (^ __nullable)(NSInteger counter,//返回YES表示不过滤,需要执行,返回NO表示抛弃这次回调
                                           NSTimeInterval responserInterval,//上次对responser的调用距离现在多久
                                           NSTimeInterval postingInterval,//上次发送message距离现在多久
                                           NSString* mesage,
                                           id parameter1,
                                           id parameter2,
                                           id parameter3,
                                           id parameter4))filter;

@end
///用KKSubscribing简化消息订阅(subscribe)接口返回值类型
#define KKSubscribing id<KKSubscribeReaction>


#pragma mark - KKReactors declaration
///主类,单例
@interface KKReactors : NSObject
///单例的简化定义
SINGLETON_H

///观察target的keyPath,发生变化后会触发回调(responser)
- (KKWatching __nullable)watch:(NSObject *)target//观察谁
                       keyPath:(NSString*)keyPath//观察路径
                     responser:(void (^ )(NSObject* __nullable oldValue,//观察回调,老值
                                          NSObject* __nullable newValue,//观察回调,新值
                                          NSObject* target,//观察回调,观察对象
                                          NSString* keyPath))responser;//观察回调,观察路径




///添加对通知name的响应(responser)
- (KKListening __nullable)listen:(NSString*)name//通知的名称
                       responser:(void (^ )(NSString* name,
                                            NSDictionary* __nullable userInfo))responser;//通知的回调,通知名称

///监听键盘出现消失通知,result返回两个参数,弥补了OC不支持多参数返回的缺陷
- (void)listenKeyBoardWithResult:(void (^ )(KKListening __nullable keyboardShowListening, KKListening __nullable keyboardHideListening))result
                       responser:(void (^ )(CGFloat keyboardHeight))responser;





///添加对自定义消息name的响应(subscribe)
- (KKSubscribing __nullable)subscribe:(NSString*)messgae//消息的名称
                            responser:(void (^ )(NSString* messgae,//消息的名称
                                                 __nullable id parameter1,//参数
                                                 __nullable id parameter2,
                                                 __nullable id parameter3,
                                                 __nullable id parameter4))responser;//消息的响应的回调,通知名称
///发送自定义消息,最多带4个参数
- (void)postMessage:(NSString*)message
         parameter1:(__nullable id)parameter1//参数1
         parameter2:(__nullable id)parameter2//参数2
         parameter3:(__nullable id)parameter3//参数3
         parameter4:(__nullable id)parameter4;//参数4

///监听电源键按下消息
- (KKSubscribing __nullable)subscribePowerButtonMessageWithResponser:(void (^ )(NSNumber* screenIsOn))responser;//screenIsOn里面是Bool,True表示屏幕亮着

@end

NS_ASSUME_NONNULL_END

源文件

//
//  KKReactors.m
//  KKReactors
//
//  Created by 龚成诗 on 2020/3/22.
//

#import "KKReactors.h"

#define kPowerButtonDidPressMessageName @"com.apple.iokit.hid.displayStatus"

#pragma mark - KKReactors declaration
@interface KKReactors()
- (void)doDisposing;
@property (nonatomic, strong, class) NSNumber* screenIsOn;
@property (nonatomic, strong, class) NSString* powerButtonMessageName;//按下电源键后,内部自动调用post接口,消息名称就是这个变量
@end

static void powerButtonDidPress(CFNotificationCenterRef center, void* observer, CFStringRef name, const void* object, CFDictionaryRef userInfo)
{
    if (KKReactors.powerButtonMessageName && CFEqual(name, (__bridge CFStringRef)KKReactors.powerButtonMessageName))
    {
        if ([KKReactors.screenIsOn boolValue])
        {
            KKReactors.screenIsOn = @NO;
        }
        else
        {
            KKReactors.screenIsOn = @YES;
        }

        [KKReactors.sharedInstance postMessage:KKReactors.powerButtonMessageName
                                    parameter1:KKReactors.screenIsOn
                                    parameter2:nil
                                    parameter3:nil
                                    parameter4:nil];
    }
}

#pragma mark - KKBaseModel
@interface KKBaseModel : NSObject <KKBaseReaction>
@property (nonatomic, weak) NSObject* companion;
@property (nonatomic, strong, readonly) NSString* stringKey;
@property (nonatomic, weak, readonly) KKReactors* reactors;
- (instancetype)initWithStringKey:(NSString*)stringKey
                         reactors:(KKReactors*)reactors;
@end

@implementation KKBaseModel

- (instancetype)initWithStringKey:(NSString*)stringKey
                         reactors:(KKReactors*)reactors
{
    self = [super init];
    if (self)
    {
        _stringKey = stringKey;
        _reactors = reactors;
    }
    return self;
}


- (void)disposeNow
{
    self.companion = nil;
    [_reactors doDisposing];
}

- (void)persistWithCompanion:(NSObject*)companion
{
    self.companion = companion;
}

@end

#pragma mark - KKWatchModel
@interface KKWatchModel : KKBaseModel <KKWatchReaction>

@end

@implementation KKWatchModel
{
    __weak KKReactors* _reactors;
    NSTimeInterval _variationTimeStamp;
    NSTimeInterval _responserTimeStamp;
    NSInteger _counter;
    BOOL (^ __nullable _filter)(NSInteger counter,
                                NSTimeInterval responserInterval,//上次对responser的调用距离现在多久
                                NSTimeInterval notificationInterval,//上次值变动距离现在多久
                                NSObject* target,
                                NSString* keyPath,
                                NSObject* __nullable oldValue,
                                NSObject* __nullable newValue);
    void(^ _responser)(NSObject* oldValue, NSObject* newValue, NSObject* target, NSString* keyPath);
}

- (instancetype)initWithTarget:(NSObject*)target
                       keyPath:(NSString*)keyPath
                     stringKey:(NSString*)stringKey
                      reactors:(KKReactors*)reactors
                     responser:(void (^)(NSObject* oldValue, NSObject* newValue, NSObject* target, NSString* keyPath))responser
{
    self = [super initWithStringKey:stringKey reactors:reactors];
    if (self)
    {
        super.companion = target;
        _variationTimeStamp = 0.0;
        _responserTimeStamp = 0.0;
        _counter = 0;
        _responser = responser;
    }
    return self;
}

- (void)installFilter:(BOOL (^ __nullable)(NSInteger counter,
                                           NSTimeInterval responserInterval,//上次对responser的调用距离现在多久
                                           NSTimeInterval notifiyingInterval,//上次通知距离现在多久
                                           NSObject* target,
                                           NSString* keyPath,
                                           NSObject* __nullable oldValue,
                                           NSObject* __nullable newValue))filter
{
    _filter = [filter copy];
}

- (void)oldValue:(NSObject*)oldValue
        newValue:(NSObject*)newValue
          target:(NSObject*)target
         keyPath:(NSString*)keyPath
{
    _counter++;
    NSTimeInterval nowTimeStamp = [[NSDate date] timeIntervalSince1970];
    NSTimeInterval variationInteval = (_variationTimeStamp == 0.0 ? 0.0 : (nowTimeStamp - _variationTimeStamp));
    NSTimeInterval responserInteval = (_responserTimeStamp == 0.0 ? 0.0 : (nowTimeStamp - _responserTimeStamp));
    
    _variationTimeStamp = nowTimeStamp;
    if (_filter
        &&
        _filter(_counter,
                responserInteval,
                variationInteval,
                target,
                keyPath,
                oldValue,
                newValue) == NO)
    {
        return;
    }
    
    _responserTimeStamp = nowTimeStamp;
    _responser(oldValue, newValue, target, keyPath);
}

@end

#pragma mark - KKWatchReactor
@interface KKWatchReactor : NSObject
@property (nonatomic, weak, readonly) NSObject* target;
@property (nonatomic, strong, readonly) NSString* stringKey;
@end

@implementation KKWatchReactor
{
    __weak KKReactors* _reactors;
    NSString* _keyPath;
    NSMutableDictionary<NSString*, KKWatchModel*>* _allWatchModels;
}

- (instancetype)initWithTarget:(NSObject*)target
                       keyPath:(NSString*)keyPath
                     stringKey:(NSString*)stringKey
                       watcher:(KKReactors*)watcher
{
    self = [super init];
    if (self) {
        _target = target;
        _keyPath = [keyPath copy];
        _stringKey = stringKey;
        _reactors = watcher;
        
        [_target addObserver:_reactors
                  forKeyPath:_keyPath
                     options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld
                     context:(void*)_stringKey.UTF8String];
    }
    return self;
}

- (void)oldValue:(NSObject*)oldValue
        newValue:(NSObject*)newValue
{
    [_allWatchModels enumerateKeysAndObjectsUsingBlock:^(NSString * _Nonnull key, KKWatchModel * _Nonnull aWatchModel, BOOL * _Nonnull stop) {
        [aWatchModel oldValue:oldValue
                     newValue:newValue
                       target:_target
                      keyPath:_keyPath];
    }];
}

- (KKWatchModel*)getModelWithResponser:(void (^ )(NSObject* oldValue,
                                                  NSObject* newValue,
                                                  NSObject* target,
                                                  NSString* keyPath))responser
{
    if (_allWatchModels == nil)
    {
        _allWatchModels = [[NSMutableDictionary alloc] init];
    }
    
    NSString* modelStringKey = [NSString stringWithFormat:@"%p", responser];
    KKWatchModel* aWatchModel = _allWatchModels[modelStringKey];
    if (aWatchModel == nil)
    {
        aWatchModel = [[KKWatchModel alloc] initWithTarget:_target
                                                   keyPath:_keyPath
                                                 stringKey:modelStringKey
                                                  reactors:_reactors
                                                 responser:responser];
    }
    _allWatchModels[aWatchModel.stringKey] = aWatchModel;
    
    return aWatchModel;
}

- (BOOL)doDisposing
{
    [_allWatchModels enumerateKeysAndObjectsUsingBlock:^(NSString * _Nonnull key, KKWatchModel * _Nonnull aWatchModel, BOOL * _Nonnull stop) {
        if (aWatchModel.companion == nil)
        {
            [_allWatchModels removeObjectForKey:aWatchModel.stringKey];
        }
    }];
    
    if (_allWatchModels.count == 0)
    {
        _allWatchModels = nil;
    }
    
    if (_target == nil || _allWatchModels == nil)
    {
        return YES;
    }
    
    return NO;
}

- (void)dealloc
{
    [_target removeObserver:_reactors
                 forKeyPath:_keyPath
                    context:(void*)_stringKey.UTF8String];
}

@end

#pragma mark - KKListenModel
@interface KKListenModel : KKBaseModel <KKListenReaction>

@end

@implementation KKListenModel
{
    __weak KKReactors* _reactors;
    NSTimeInterval         _notificationInterval;
    NSTimeInterval _responserTimeStamp;
    NSInteger _counter;
    BOOL (^ __nullable _filter)(NSInteger counter,
                                NSTimeInterval responserInterval,//上次对responser的调用距离现在多久
                                NSTimeInterval notificationInterval,//上次值变动距离现在多久
                                NSString* name,
                                NSDictionary* userInfo);
    void(^ _responser)(NSString* name, NSDictionary* userInfo);
}

- (instancetype)initWithName:(NSString*)name
                   stringKey:(NSString*)stringKey
                    reactors:(KKReactors*)reactors
                   responser:(void (^)(NSString* name, NSDictionary* userInfo))responser
{
    self = [super initWithStringKey:stringKey reactors:reactors];
    if (self)
    {
        super.companion = name;
        _notificationInterval = 0.0;
        _responserTimeStamp = 0.0;
        _counter = 0;
        _responser = responser;
    }
    return self;
}

- (void)installFilter:(BOOL (^ __nullable)(NSInteger counter,
                                           NSTimeInterval responserInterval,//上次对responser的调用距离现在多久
                                           NSTimeInterval notificationInterval,//上次值变动距离现在多久
                                           NSString* name))filter
{
    _filter = [filter copy];
}

- (void)onNotification:(NSString*)name
              userInfo:(NSDictionary*)userInfo
{
    _counter++;
    NSTimeInterval nowTimeStamp = [[NSDate date] timeIntervalSince1970];
    NSTimeInterval variationInteval = (_notificationInterval == 0.0 ? 0.0 : (nowTimeStamp - _notificationInterval));
    NSTimeInterval responserInteval = (_responserTimeStamp == 0.0 ? 0.0 : (nowTimeStamp - _responserTimeStamp));
    
            _notificationInterval = nowTimeStamp;
    if (_filter
        &&
        _filter(_counter,
                responserInteval,
                variationInteval,
                name,
                userInfo) == NO)
    {
        return;
    }
    
    _responserTimeStamp = nowTimeStamp;
    _responser(name, userInfo);
}

@end

#pragma mark - KKListenReactor
@interface KKListenReactor : NSObject
@property (nonatomic, strong, readonly) NSString* name;
@property (nonatomic, strong, readonly) NSString* stringKey;
@end

@implementation KKListenReactor
{
    __weak KKReactors* _reactors;
    NSString* _name;
    NSMutableDictionary<NSString*, KKListenModel*>* _allListenModels;
}

- (instancetype)initWithName:(NSString*)name
                   stringKey:(NSString*)stringKey
                     watcher:(KKReactors*)watcher
          notificationAction:(SEL)notificationAction
{
    self = [super init];
    if (self)
    {
        _stringKey = stringKey;
        _reactors = watcher;
        _name = name;
        
        [NSNotificationCenter.defaultCenter addObserver:_reactors
                                               selector:notificationAction
                                                   name:name
                                                 object:nil];
    }
    return self;
}

- (void)onNotification:(NSDictionary *)userInfo
{
    [_allListenModels enumerateKeysAndObjectsUsingBlock:^(NSString * _Nonnull key, KKListenModel * _Nonnull aListenModel, BOOL * _Nonnull stop) {
        [aListenModel onNotification:_name userInfo:userInfo];
    }];
}

- (KKListenModel*)getModelWithResponser:(void (^ )(NSString* name, NSDictionary* userInfo))responser
{
    if (_allListenModels == nil)
    {
        _allListenModels = [[NSMutableDictionary alloc] init];
    }
    
    NSString* modelStringKey = [NSString stringWithFormat:@"%p", responser];
    KKListenModel* aListenModel = _allListenModels[modelStringKey];
    if (aListenModel == nil)
    {
        aListenModel = [[KKListenModel alloc] initWithName:_name
                                                 stringKey:modelStringKey
                                                  reactors:_reactors
                                                 responser:responser];
    }
    
    _allListenModels[aListenModel.stringKey] = aListenModel;
    
    return aListenModel;
}

- (BOOL)doDisposing
{
    [_allListenModels enumerateKeysAndObjectsUsingBlock:^(NSString * _Nonnull key, KKListenModel * _Nonnull aListenModel, BOOL * _Nonnull stop) {
        if (aListenModel.companion == nil)
        {
            [_allListenModels removeObjectForKey:aListenModel.stringKey];
        }
    }];
    
    if (_allListenModels.count == 0)
    {
        _allListenModels = nil;
    }
    
    if (_allListenModels == nil)
    {
        return YES;
    }
    
    return NO;
}

- (void)dealloc
{
    [NSNotificationCenter.defaultCenter removeObserver:_reactors
                                                  name:_name
                                                object:nil];
}

@end

#pragma mark - KKSubscribeModel
@interface KKSubscribeModel : KKBaseModel <KKSubscribeReaction>

@end

@implementation KKSubscribeModel
{
    __weak KKReactors* _reactors;
    NSTimeInterval _postingTimeStamp;
    NSTimeInterval _responserTimeStamp;
    NSInteger _counter;
    id _parameter1;
    id _parameter2;
    id _parameter3;
    id _parameter4;
    BOOL (^ __nullable _filter)(NSInteger counter,
                                NSTimeInterval responserInterval,//上次对responser的调用距离现在多久
                                NSTimeInterval postingInterval,//上次发送message距离现在多久
                                NSString* message,
                                id parameter1,
                                id parameter2,
                                id parameter3,
                                id parameter4);
    void(^ _responser)(NSString* message,
                       id parameter1,
                       id parameter2,
                       id parameter3,
                       id parameter4);
}

- (instancetype)initWithMessage:(NSString*)message
                      stringKey:(NSString*)stringKey
                       reactors:(KKReactors*)reactors
                      responser:(void (^)(NSString* message,
                                          id parameter1,
                                          id parameter2,
                                          id parameter3,
                                          id parameter4))responser
{
    self = [super initWithStringKey:stringKey reactors:reactors];
    if (self)
    {
        super.companion = message;
        _postingTimeStamp = 0.0;
        _responserTimeStamp = 0.0;
        _counter = 0;
        _responser = responser;
    }
    return self;
}

- (void)installFilter:(BOOL (^ __nullable)(NSInteger counter,
                                           NSTimeInterval responserInterval,//上次对responser的调用距离现在多久
                                           NSTimeInterval postingInterval,//上次发送message距离现在多久
                                           NSString* mesage,
                                           id parameter1,
                                           id parameter2,
                                           id parameter3,
                                           id parameter4))filter
{
    _filter = [filter copy];
}

- (void)onMessage:(NSString*)message
       parameter1:(id)parameter1
       parameter2:(id)parameter2
       parameter3:(id)parameter3
       parameter4:(id)parameter4
{
    _counter++;
    NSTimeInterval nowTimeStamp = [[NSDate date] timeIntervalSince1970];
    NSTimeInterval postingInteval = (_postingTimeStamp == 0.0 ? 0.0 : (nowTimeStamp - _postingTimeStamp));
    NSTimeInterval responserInteval = (_responserTimeStamp == 0.0 ? 0.0 : (nowTimeStamp - _responserTimeStamp));
    
    _postingTimeStamp = nowTimeStamp;
    if (_filter
        &&
        _filter(_counter,
                responserInteval,
                postingInteval,
                message,
                parameter1,
                parameter2,
                parameter2,
                parameter4) == NO)
    {
        return;
    }
    
    _responserTimeStamp = nowTimeStamp;
    _responser(message,
               parameter1,
               parameter2,
               parameter3,
               parameter4);
}

@end

#pragma mark - KKSubscribeReactor
@interface KKSubscribeReactor : NSObject
@property (nonatomic, strong, readonly) NSString* message;
@property (nonatomic, strong, readonly) NSString* stringKey;
@end

@implementation KKSubscribeReactor
{
    __weak KKReactors* _reactors;
    NSString* _message;
    NSMutableDictionary<NSString*, KKSubscribeModel*>* _allSubscribeModels;
}

- (instancetype)initWithMessage:(NSString*)message
                      stringKey:(NSString*)stringKey
                        watcher:(KKReactors*)watcher
{
    self = [super init];
    if (self)
    {
        _stringKey = stringKey;
        _reactors = watcher;
        _message = message;
    }
    return self;
}

- (void)parameter1:(id)parameter1
        parameter2:(id)parameter2
        Parameter3:(id)parameter3
        Parameter4:(id)parameter4
{
    [_allSubscribeModels enumerateKeysAndObjectsUsingBlock:^(NSString * _Nonnull key, KKSubscribeModel * _Nonnull aListenModel, BOOL * _Nonnull stop) {
        [aListenModel onMessage:_message
                     parameter1:parameter1
                     parameter2:parameter2
                     parameter3:parameter3
                     parameter4:parameter4];
    }];
}

- (KKSubscribeModel*)getModelWithResponser:(void (^ )(NSString* message,
                                                      id parameter1,
                                                      id parameter2,
                                                      id parameter3,
                                                      id parameter4))responser
{
    if (_allSubscribeModels == nil)
    {
        _allSubscribeModels = [[NSMutableDictionary alloc] init];
    }
    
    NSString* modelStringKey = [NSString stringWithFormat:@"%p", responser];
    NSLog(@"block = %@", modelStringKey);
    KKSubscribeModel* aSubscribeModel = _allSubscribeModels[modelStringKey];
    if (aSubscribeModel == nil)
    {
        aSubscribeModel = [[KKSubscribeModel alloc] initWithMessage:_message
                                                          stringKey:modelStringKey
                                                           reactors:_reactors
                                                          responser:responser];
    }
    
    _allSubscribeModels[aSubscribeModel.stringKey] = aSubscribeModel;
    
    return aSubscribeModel;
}

- (BOOL)doDisposing
{
    [_allSubscribeModels enumerateKeysAndObjectsUsingBlock:^(NSString * _Nonnull key, KKSubscribeModel * _Nonnull aSubscribeModel, BOOL * _Nonnull stop) {
        if (aSubscribeModel.companion == nil)
        {
            [_allSubscribeModels removeObjectForKey:aSubscribeModel.stringKey];
        }
    }];
    
    if (_allSubscribeModels.count == 0)
    {
        _allSubscribeModels = nil;
    }
    
    if (_allSubscribeModels == nil)
    {
        return YES;
    }
    
    return NO;
}

- (void)dealloc
{
    
}

@end

#pragma mark - KKReactors
@implementation KKReactors
{
    NSMutableDictionary<NSString*, KKWatchReactor*>* _allWatchReactors;//所有的观察活动
    NSMutableDictionary<NSString*, KKListenReactor*>* _allListenReactors;//所有通知的监听活动
    NSMutableDictionary<NSString*, KKSubscribeReactor*>* _allSubscribeReactors;//所有对消息的订阅活动
}
SINGLETON_M;

static NSNumber* s_screenIsOn;

+ (void)setScreenIsOn:(NSNumber *)screenIsOn
{
    s_screenIsOn = screenIsOn;
}

+ (NSNumber*)screenIsOn
{
    return s_screenIsOn;
}

static NSString* s_powerButtonMessageName;

+ (void)setPowerButtonMessageName:(NSString *)powerButtonMessageName
{
    s_powerButtonMessageName = powerButtonMessageName;
}

+ (NSString*)powerButtonMessageName
{
    return s_powerButtonMessageName;
}

- (KKWatchReactor*)getWatchReactorWithTarget:(NSObject*)target
                                     keyPath:(NSString*)keyPath
{
    if (_allWatchReactors == nil)
    {
        _allWatchReactors = [[NSMutableDictionary alloc] init];
    }
    
    NSString* reactorStringKey = [NSString stringWithFormat:@"%p-%@", target, keyPath];
    KKWatchReactor* aWatchReactor = _allWatchReactors[reactorStringKey];
    if (aWatchReactor == nil)
    {
        aWatchReactor = [[KKWatchReactor alloc] initWithTarget:target
                                                       keyPath:keyPath
                                                     stringKey:reactorStringKey
                                                       watcher:self];
    }
    _allWatchReactors[aWatchReactor.stringKey] = aWatchReactor;
    return aWatchReactor;
}

- (KKWatching __nullable)watch:(NSObject *)target
                       keyPath:(NSString*)keyPath
                     responser:(void (^ )(NSObject* oldValue,
                                          NSObject* newValue,
                                          NSObject* target,
                                          NSString* keyPath))responser
{
    [self doDisposing];
    
    if (target == nil || keyPath.length == 0 || responser == nil)
    {
        return nil;
    }
    
    KKWatchReactor* aWatchReactor = [self getWatchReactorWithTarget:target
                                                            keyPath:keyPath];
    
    KKWatchModel* aWatchModel = [aWatchReactor getModelWithResponser:[responser copy]];
    
    return aWatchModel;
}

- (void)observeValueForKeyPath:(NSString *)keyPath
                      ofObject:(id)object
                        change:(NSDictionary *)change
                       context:(void *)context
{
    [self doDisposing];
    
    char* chReactorStringKey = (char*)context;
    NSString* reactorStringKey = [NSString stringWithUTF8String:chReactorStringKey];
    
    KKWatchReactor* aWatchReactor = _allWatchReactors[reactorStringKey];
    if (aWatchReactor == nil)
    {
        return;
    }
    
    [aWatchReactor oldValue:change[@"old"] newValue:change[@"new"]];
}

- (KKListenReactor*)getListenReactorWithName:(NSString*)name
{
    if (_allListenReactors == nil)
    {
        _allListenReactors = [[NSMutableDictionary alloc] init];
    }
    
    NSString* reactorStringKey = name;
    KKListenReactor* aListenReactor = _allListenReactors[reactorStringKey];
    if (aListenReactor == nil)
    {
        aListenReactor = [[KKListenReactor alloc] initWithName:name
                                                     stringKey:reactorStringKey
                                                       watcher:self
                                            notificationAction:@selector(onNotification:)];
    }
    _allListenReactors[aListenReactor.stringKey] = aListenReactor;
    return aListenReactor;
}

- (KKListening __nullable)listen:(NSString*)name//通知的名称
                       responser:(void (^ )(NSString* name, NSDictionary* userInfo))responser//通知的回调,通知名称
{
    [self doDisposing];
    
    if (name.length == 0 || responser == nil)
    {
        return nil;
    }
    
    KKListenReactor* aListenReactor = [self getListenReactorWithName:name];
    
    KKListenModel* aListenModel = [aListenReactor getModelWithResponser:[responser copy]];
    
    return aListenModel;
}

- (void)onNotification:(NSNotification*)notification
{
    [self doDisposing];
    
    NSString* name = notification.name;
    NSDictionary* userInfo = notification.userInfo;
    if (name.length == 0)
    {
        return;
    }
    [_allListenReactors[name] onNotification:userInfo];
}

///监听键盘出现消失通知
- (void)listenKeyBoardWithResult:(void (^ )(KKListening keyboardShowListening, KKListening keyboardHideListening))result
                       responser:(void (^ )(CGFloat keyboardHeight))responser
{
    if (result == nil)
    {
        return;
    }
    KKListening keyboardShowListening = [self listen:UIKeyboardWillShowNotification responser:^(NSString * _Nonnull name, NSDictionary * _Nullable userInfo) {
            CGRect keyboardRect = [userInfo[UIKeyboardFrameEndUserInfoKey] CGRectValue];
            responser(keyboardRect.size.height);
        }];

    KKListening keyboardHideListening = [self listen:UIKeyboardWillHideNotification responser:^(NSString * _Nonnull name, NSDictionary * _Nullable userInfo) {
            responser(0.0f);
        }];

    result(keyboardShowListening, keyboardHideListening);
}

- (KKSubscribeReactor*)getSubscribeReactorWithName:(NSString*)message
{
    if (_allSubscribeReactors == nil)
    {
        _allSubscribeReactors = [[NSMutableDictionary alloc] init];
    }
    
    NSString* reactorStringKey = message;
    KKSubscribeReactor* aSubscribeReactor = _allSubscribeReactors[reactorStringKey];
    if (aSubscribeReactor == nil)
    {
        aSubscribeReactor = [[KKSubscribeReactor alloc] initWithMessage:message
                                                              stringKey:reactorStringKey
                                                                watcher:self];
    }
    _allSubscribeReactors[aSubscribeReactor.stringKey] = aSubscribeReactor;
    return aSubscribeReactor;
}

///添加对自定义消息name的响应(subscribe)
- (KKSubscribing __nullable)subscribe:(NSString*)messgae//消息的名称
                            responser:(void (^ )(NSString* messgae,//消息的名称
                                                 id parameter1,//参数
                                                 id parameter2,
                                                 id parameter3,
                                                 id parameter4))responser//消息的响应的回调,通知名称
{
    [self doDisposing];
    
    if (messgae.length == 0 || responser == nil)
    {
        return nil;
    }
    
    KKSubscribeReactor* aSubscribeReactor = [self getSubscribeReactorWithName:messgae];
    
    KKSubscribeModel* aSubscribeModel = [aSubscribeReactor getModelWithResponser:[responser copy]];
    
    return aSubscribeModel;
}

- (void)postMessage:(NSString*)message
         parameter1:(id)parameter1//参数1
         parameter2:(id)parameter2//参数2
         parameter3:(id)parameter3//参数3
         parameter4:(id)parameter4//参数4
{
    [self doDisposing];
    
    if (message.length == 0)
    {
        return;
    }
    [_allSubscribeReactors[message] parameter1:parameter1
                                    parameter2:parameter2
                                    Parameter3:parameter3
                                    Parameter4:parameter4];
}

- (void)doDisposing
{
    [_allWatchReactors enumerateKeysAndObjectsUsingBlock:^(NSString * _Nonnull key, KKWatchReactor * _Nonnull aWatchReactor, BOOL * _Nonnull stop) {
        
        if ([aWatchReactor doDisposing])
        {
            [_allWatchReactors removeObjectForKey:aWatchReactor.stringKey];
        }
        
    }];
    
    if (_allWatchReactors.count == 0)
    {
        _allWatchReactors = nil;
    }
    
    
    [_allListenReactors enumerateKeysAndObjectsUsingBlock:^(NSString * _Nonnull key, KKListenReactor * _Nonnull aListenReactor, BOOL * _Nonnull stop) {
        
        if ([aListenReactor doDisposing])
        {
            [_allListenReactors removeObjectForKey:aListenReactor.stringKey];
        }
        
    }];
    
    if (_allListenReactors.count == 0)
    {
        _allListenReactors = nil;
    }
    
    [_allSubscribeReactors enumerateKeysAndObjectsUsingBlock:^(NSString * _Nonnull key, KKSubscribeReactor * _Nonnull aSubscribeReactor, BOOL * _Nonnull stop) {
        
        if ([aSubscribeReactor doDisposing])
        {
            [_allSubscribeReactors removeObjectForKey:aSubscribeReactor.stringKey];
        }
        
    }];
    
    if (_allSubscribeReactors.count == 0)
    {
        _allSubscribeReactors = nil;
    }
}

///监听电源键按下消息
- (KKSubscribing __nullable)subscribePowerButtonMessageWithResponser:(void (^ )(NSNumber* screenIsOn))responser//screenIsOn里面是Bool,True表示屏幕亮着
{
    if (responser == nil)
    {
        return nil;
    }

    if (KKReactors.powerButtonMessageName == nil)
    {
        KKReactors.screenIsOn = @YES;
        KKReactors.powerButtonMessageName = kPowerButtonDidPressMessageName;
        //注册锁屏通知
        CFNotificationCenterAddObserver(CFNotificationCenterGetDarwinNotifyCenter(),
                                        NULL,
                                        powerButtonDidPress,
                                        (__bridge CFStringRef)KKReactors.powerButtonMessageName,
                                        NULL,
                                        CFNotificationSuspensionBehaviorDeliverImmediately);
    }

    return [self subscribe:KKReactors.powerButtonMessageName
                 responser:^(NSString * _Nonnull messgae, id  _Nullable parameter1, id  _Nullable parameter2, id  _Nullable parameter3, id  _Nullable parameter4) {
            responser(parameter1);
    }];
}

@end

调用举例

Person* _person = Person.new;
    KKReactors watching =
    [KKReactors.sharedInstance watchTarget:_person
                                   atPath:@"name"
                                responser:^(NSObject * __nullable oldValue, NSObject * __nullable newValue, NSObject * _Nonnull target, NSString * _Nonnull keyPath) {
        NSLog(@"%@   %@", oldValue, newValue);
    }];

    [watching installFilter:^BOOL(NSInteger counter,
                                 NSTimeInterval responserInterval,
                                 NSTimeInterval variationInterval,
                                 NSObject* target,
                                 NSString* keyPath,
                                 NSObject * _Nullable oldValue,
                                 NSObject * _Nullable newValue) {
        if (counter < 2)//只需要响应两次
        {
            return YES;
        }

        return NO;
    }];
    
    _person.name = @"abc";
    _person.name = @"def";
    _person.name = @"ghi";
    [watching persistWithCompanion:self];//当前VC释放后,KVO跟着一起释放

特别说明

1、代码中涉及到的SINGLETON_H和SINGLETON_M两个宏,请参考我的前面一篇博客。
2、block里面引用到self,一定要weak化,建议使用我上篇博客里提到的WEAK和STRONG宏
3、代码中运用同样的思路,也改造了NSNotification,使得NSNotification也支持函数式调用,参考代码中的 listen:reponsor:接口
4、代码中运用同样的思路,支持自定义小的发送和接受,参考subscribe接口和postMessage接口
5、代码中还封装了对键盘出现消失通知的封装,参考listenKeyBoardWithResult接口。这个接口用Block的方式,返回2个值,解决了OC不支持返回多个返回值的缺陷

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值