iOS中的crash防护(三)KVO造成的crash

  最近写的JKCrashProtect的两篇文章得到了一些小伙伴的响应,一些小伙伴已经开始使用JKCrashProtect这个库了,很是开心。我今天在这里重点给大家分享一下有KVO造成的crash。

KVO产生crash的原因

  相信大家用过KVO的应该比较多,KVO中的添加观察者,和移除观察者必须要成对出现,这个常识相信大家都是有的,所以某个人如果忘记了使用后移除已经添加的观察者造成了crash,这个不在我的考虑范围内。原因如下:JKCrashProtect是为了处理一些比较繁琐,同时容易造成crash的场景,但前提是不改变开发者的编码习惯,不能让开发者养成不好的开发习惯。既然观察者的添加和移除要成对的出现,那么如果不成对出现,就会出现crash现象。比如多线程下添加了相同的两个观察者,但移除的时候只移除了一个;或者添加的一个观察者,移除的时候由于多线程的原因却移除了两次。总之只要不是添加和移除不是成对的出现,那么就会出现crash。出现crash现象的实例代码如下:

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.
    self.view.backgroundColor = [UIColor whiteColor];
    _jack = [JKPerson new];
 [_jack addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionNew context:nil];
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
      [_jack addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionNew context:nil];
    });

    UIButton *button = [UIButton new];
    button.frame = CGRectMake(0, 0, 80, 30);
    button.center = self.view.center;
    button.backgroundColor = [UIColor redColor];
    [button setTitle:@"Click" forState:UIControlStateNormal];
    [button addTarget: self action:@selector(buttonClicked:) forControlEvents:UIControlEventTouchUpInside];
    [self.view addSubview:button];

}


- (void)dealloc{

    [_jack removeObserver:self forKeyPath:@"name"];

}

大家可以尝试运行下代码,看一下效果。

解决方案

  为了解决这个问题,在网上查找了很多资料,有的大神的思路是建立一个delegate,观察者和被观察者通过delegate间接建立联系,由于没有demo源码,我这边也不是太理解,而且觉得这种方案比较繁琐,后来考虑建立一个哈希表,用来保存,观察者,keyPath的信息,如果哈希表里已经有了相关的观察者,keyPath信息,那么继续添加观察者的话,就不载进行添加,同样移除观察的时候,也现在哈希表中进行查找,如果存在观察者,keypath信息,那么移除,如果没有的话就不执行相关的移除操作。要实现这样的思路就需要用到methodSwizzle来进行方法交换。我这通过写了一个NSObject的cagegory来进行方法交换。示例代码如下:

#pragma mark --- KVOCrashProtect

- (void)setKVOHashTable:(NSHashTable *)KVOHasTable{
    objc_setAssociatedObject(self, &KVOHashTableKey, KVOHasTable, OBJC_ASSOCIATION_RETAIN);
}


- (NSHashTable *)KVOHashTable{

    return objc_getAssociatedObject(self, &KVOHashTableKey);

}

- (void)JKCrashProtectaddObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(nullable void *)context{
    if ([observer isKindOfClass:[UIViewController class]]) {

        @synchronized (self) {
            NSInteger kvoHash = [self _JKCrashProtectHash:observer :keyPath];


            if (!self.KVOHashTable) {
                self.KVOHashTable = [NSHashTable hashTableWithOptions:NSPointerFunctionsStrongMemory];
            }

        if (![self.KVOHashTable containsObject:@(kvoHash)]) {
            [self.KVOHashTable addObject:@(kvoHash)];
            [self JKCrashProtectaddObserver:observer forKeyPath:keyPath options:options context:context];
        }

    }

    }else{

        [self JKCrashProtectaddObserver:observer forKeyPath:keyPath options:options context:context];

    }

}

当然了,对于KVO造成crash的原因,我这边可能分析的不够全面,欢迎大家多多指教,如果大家发现在使用KVO的时候还有别的原因造成crash欢迎给我留言啊,大家一块处理。JKCrashProtect源码
  另外最近在完善JKCrashProtect的时候,觉得如果想要很好的解决或者避免Crash产生,那么我们需要对产生crash的原因,进行提前了解、分析,这样即使遇到了crash,我们也不会慌张。

©️2020 CSDN 皮肤主题: 大白 设计师: CSDN官方博客 返回首页
实付0元
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值