美文网首页
『ios』kvo中安全的移除监听

『ios』kvo中安全的移除监听

作者: butterflyer | 来源:发表于2021-06-07 20:39 被阅读0次

是否经常在项目中遇到kvo移除崩溃的错误?
其实我们可以用try catch来解决这个问题。
今天看到了二种比较优雅的解决办法。

+ (void)load
{
    [self switchMethod];
}

+ (void)switchMethod
{
    SEL removeSel = @selector(removeObserver:forKeyPath:);
    SEL myRemoveSel = @selector(removeSafe:forKeyPath:);
    SEL addSel = @selector(addObserver:forKeyPath:options:context:);
    SEL myaddSel = @selector(addSafe:forKeyPath:options:context:);
    
    Method systemRemoveMethod = class_getClassMethod([self class],removeSel);
    Method DasenRemoveMethod = class_getClassMethod([self class], myRemoveSel);
    Method systemAddMethod = class_getClassMethod([self class],addSel);
    Method DasenAddMethod = class_getClassMethod([self class], myaddSel);
    
    method_exchangeImplementations(systemRemoveMethod, DasenRemoveMethod);
    method_exchangeImplementations(systemAddMethod, DasenAddMethod);
}

// 交换后的方法
- (void)removeSafe:(NSObject *)observer forKeyPath:(NSString *)keyPath
{
    if ([self observerKeyPath:keyPath observer:observer]) {
        [self removeSafe:observer forKeyPath:keyPath];
    }
}

// 交换后的方法
- (void)addSafe:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(void *)context
{
    if (![self observerKeyPath:keyPath observer:observer]) {
        [self addSafe:observer forKeyPath:keyPath options:options context:context];
    }
}


// 进行检索获取Key
- (BOOL)observerKeyPath:(NSString *)key observer:(id )observer
{
    id info = self.observationInfo;
    NSArray *array = [info valueForKey:@"_observances"];
    for (id objc in array) {
        id Properties = [objc valueForKeyPath:@"_property"];
        id newObserver = [objc valueForKeyPath:@"_observer"];
        
        NSString *keyPath = [Properties valueForKeyPath:@"_keyPath"];
        if ([key isEqualToString:keyPath] && [newObserver isEqual:observer]) {
            return YES;
        }
    }
    return NO;
}

我们可以利用

    id info = self.observationInfo;
    NSArray *array = [info valueForKey:@"_observances"];

拿到当前类进行kvo监听到对象。如下图所示

image.png

然后通过对比监听到key和监听的对象是否相同。

 id Properties = [objc valueForKeyPath:@"_property"];
        id newObserver = [objc valueForKeyPath:@"_observer"];
image.png

还有一种解决方案。

@interface ObserverData : NSObject
@property (nonatomic, strong)id objc;
@property (nonatomic, copy)  NSString *keyPath;
- (instancetype)initWithObjc:(id)objc key:(NSString *)key;

@end
@implementation ObserverData
- (instancetype)initWithObjc:(id)objc key:(NSString *)key
{
    if (self = [super init]) {
        self.objc = objc;
        self.keyPath = key;
    }
    return self;
}
@end

#import "DSObserver.h"
@implementation DSObserver
+ (instancetype)sharedDSObserver
{
    static id objc;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        objc = [NSMutableArray array];
    });
    return objc;
}
@end


#pragma mark - 第二种方案,利用私有属性
// 交换后的方法
- (void)removeSafe:(NSObject *)observer forKeyPath:(NSString *)keyPath
{
    NSMutableArray *Observers = [DSObserver sharedDSObserver];
    ObserverData *userPathData = [self observerKeyPath:keyPath];
    // 如果有该key值那么进行删除
    if (userPathData) {
        [Observers removeObject:userPathData];
        [self removeSafe:observer forKeyPath:keyPath];
    }
    return;
}

// 交换后的方法
- (void)addSafe:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(void *)context
{
    ObserverData *userPathData= [[ObserverData alloc]initWithObjc:self key:keyPath];
    NSMutableArray *Observers = [DSObserver sharedDSObserver];

    // 如果没有注册,那么才进行注册
    if (![self observerKeyPath:keyPath]) {
        [Observers addObject:userPathData];
        [self addSafe:observer forKeyPath:keyPath options:options context:context];
    }

}
//
// 进行检索,判断是否已经存储了该Key值
- (ObserverData *)observerKeyPath:(NSString *)keyPath
{
    NSMutableArray *Observers = [DSObserver sharedDSObserver];
    for (ObserverData *data in Observers) {
        if ([data.objc isEqual:self] && [data.keyPath isEqualToString:keyPath]) {
            return data;
        }
    }
    return nil;
}

可以新建一个全局的管理kvo监听管理者数组,然后如果监听就add,移除就remove,进行之前,进行判断是否已经存在或者是已经移除,来避免问题。
其实我比较推荐第二种解决方案。因为第一种应该会有一些特殊情况不能完全覆盖到。

相关文章

  • 『ios』kvo中安全的移除监听

    是否经常在项目中遇到kvo移除崩溃的错误?其实我们可以用try catch来解决这个问题。今天看到了二种比较优雅的...

  • ARC下-dealloc方法

    ARC下,覆写-dealloc方法: (void)dealloc { // 移除通知中心的监听// 移除KVO监听...

  • KVO监听scrollView的滚动方向

    设置监听 监听回调处理 移除监听 利用KVO来监听scrollView类contentOffset的变化

  • KVO

    KVO键值观察 KVO三部曲: 添加监听 监听回调 移除监听(很重要,一定不能忘记) KVO可以对摸一个属性进行监...

  • Objective-C 语言特性之KVO

    对于KVO的基本使用这里不再做阐述,使用时,记得添加监听和移除监听成对出现,并在合适的时机再将监听移除即可。这...

  • iOS Runtime学习笔记 (二) - 实战应用

    iOS runtime实战应用 iOS runtime 进行添加属性,并支持KVO监听 iOS 中category...

  • iOS 如何自动移除KVO观察者

    iOS 如何自动移除KVO观察者

  • iOS - 关于 KVO 的一些总结

    目录1. 什么是 KVO2. KVO 的基本使用 2.1 注册方法 2.2 监听方法 2.3 移除方法 2.4 使...

  • KVO - NSKeyValueObserving

    KVO: 三个步骤: 1. 注册监听 2. 使用回调,对监听对象处理 3. 移除监听 // 使用UITextFie...

  • KVO学习笔记

    1.KVO初探学习2.KVO 底层原理探索 1.KVO初探学习 移除观察者的重要性 (IOS11之后说不移除是不对...

网友评论

      本文标题:『ios』kvo中安全的移除监听

      本文链接:https://www.haomeiwen.com/subject/fpkreltx.html