iOS 永久解决KVO重复移除监听导致闪退问题

发布时间 2023-03-22 21:16:52作者: qqcc1388

kvo是成对出现的,如果添加了监听,就要到必要的时候移除监听,如果重复移除监听就会导致闪退问题,解决方法也很简单,通过一个对象来管理kvo的添加和移除,添加了kvo就保存起来,移除的时候先拿到添加的列表,移除已经添加的,如果已经移除过,则return防止重复移除导致闪退,具体代码如下:


@interface KVOProxy : NSObject

/// 添加观察者
/// - Parameters:
///   - target: target
///   - observer: observer
///   - keypath: keypath
///   - options: options
///   - context: context
-(void)px_addObserverWithTarget:(NSObject *)target
                       observer:(NSObject *)observer
                        keypath:(NSString *)keypath
                        options:(NSKeyValueObservingOptions)options
                        context:(nullable void *)context;



/// 移除观察者
/// - Parameters:
///   - target: target
///   - observer: observer
///   - keypath: keypath
-(void)px_removeObserverWithTarget:(NSObject *)target
                          observer:(NSObject *)observer
                           keypath:(NSString *)keypath;

@end

#import "KVOProxy.h"

@interface KVOObject :NSObject

@property (nonatomic,copy) NSString *keypath;

@property (nonatomic,strong) NSObject *observer;

@property (nonatomic,strong) NSObject *target;

@end

@implementation KVOObject

-(instancetype)initWithTarget:(NSObject *)target keypath:(NSString *)keypath observer:(NSObject *)observer{
    if(self = [super init]){
        self.keypath = keypath;
        self.target = target;
        self.observer = observer;
    }
    return self;
}

@end


@interface KVOProxy ()

@property (nonatomic,strong) NSMutableArray<KVOObject *> *kvoInfo;

@property (nonatomic,strong) NSLock *lock;

@end

@implementation KVOProxy


-(void)px_addObserverWithTarget:(NSObject *)target observer:(NSObject *)observer keypath:(NSString *)keypath options:(NSKeyValueObservingOptions)options context:(nullable void *)context{
    if(keypath.length == 0) return;
    /// 加锁
    [self.lock lock];
    //判断是否是同一个target添加重复的keypath,已存在相同的记录就直接返回,不作处理
    for (KVOObject *obj in self.kvoInfo) {
        if([target isEqual:obj.target] && [keypath isEqualToString:obj.keypath]){
            [self.lock unlock];
            return;
        }
    }
    /// 保存当前的kvo信息
    [self.kvoInfo addObject:[[KVOObject alloc] initWithTarget:target keypath:keypath observer:observer]];
    /// 添加监听
    [target addObserver:observer forKeyPath:keypath options:options context:context];
    [self.lock unlock];
}

-(void)px_removeObserverWithTarget:(NSObject *)target observer:(NSObject *)observer keypath:(NSString *)keypath{
    if(keypath.length == 0) return;
    [self.lock lock];

    KVOObject *kvoObj;
    for (KVOObject *obj in self.kvoInfo) {
        if(([target isEqual:obj.target] && [keypath isEqualToString:obj.keypath])){
            kvoObj = obj;
        }
    }
    /// 如果没有找到直接return
    if(!kvoObj){
        [self.lock unlock];
        return;
    }
    [self.kvoInfo removeObject:kvoObj];
    /// 移除监听
    [target removeObserver:observer forKeyPath:keypath];
    [self.lock unlock];
}

#pragma mark - Lazy

-(NSMutableArray<KVOObject *> *)kvoInfo{
    if(!_kvoInfo){
        _kvoInfo = [NSMutableArray array];
    }
    return _kvoInfo;
}


-(NSLock *)lock{
    if(!_lock){
        _lock = [[NSLock alloc] init];
    }
    return _lock;
}

@end

具体使用:

@property (nonatomic,strong) KVOProxy *kvoProxy;

AVCaptureDevice * camDevice = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
[self.kvoProxy px_addObserverWithTarget:camDevice observer:self keypath:@"adjustingFocus" options:NSKeyValueObservingOptionNew context:nil];
[self.kvoProxy px_removeObserverWithTarget:camDevice observer:self keypath:@"adjustingFocus"];