我看人间事,热爱作首诗程序员Happy iOS

iOS KVO使用小坑

2018-05-28  本文已影响20人  tongxyj

这个版本接手selfStrong的代码的时候,在移除KVO的observer的时候一直挂,经排查发现,原来这个KVO是在父类中注册的和移除的,在某一时刻有个子类的实例被释放了,走到了父类的dealloc方法里,就挂了,因为子类并没有注册这个KVO,写了个demo测试下,顺便也看看通知会不会有这样的情况:

@interface Father : NSObject
- (void)addKVO;
- (void)addNotification;
@end
#import "Father.h"

@implementation Father

- (void)addKVO {
    [self addObserver:self forKeyPath:@"testKVOKey" options:NSKeyValueObservingOptionOld | NSKeyValueObservingOptionNew context:nil];
}
- (void)addNotification {
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(testNoti) name:@"testNoti" object:nil];
}
- (void)dealloc {
    [self removeObserver:self forKeyPath:@"testKVOKey"];
     [[NSNotificationCenter defaultCenter] removeObserver:self name:@"testNoti" object:nil];
}

有个父类Father,在Father类中注册了一个KVO和通知,并在dealloc方法中移除对应的KVO和通知。

#import "Father.h"

@interface Son : Father
@end
#import "Son.h"

@implementation Son
@end

还有个子类Son,啥也没干。

#import "ViewController.h"
#import "Son.h"
@interface ViewController ()
@property (nonatomic, strong) Father *father;
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    self.father = [[Father alloc] init];
    [self.father addKVO];//注册KVO
    [self.father addNotification];//注册通知
    Son *son = [[Son alloc] init];
}
@end

在VC中分别创建了父类和子类的实例,并且注册了父类的KVO和通知。
程序运行,奔溃,控制台报错:
*** Terminating app due to uncaught exception 'NSRangeException', reason: 'Cannot remove an observer <Son 0x6000002095e0> for the key path "testKVOKey" from <Son 0x6000002095e0> because it is not registered as an observer.'
原因很明显,Son被释放,调用了Father的dealloc,移除了一个没有注册的KVO,就会奔溃,但通知却没有这个问题,另外KVO移除多次或者没有移除监听也会奔溃。对于通知,多次移除也不会奔溃,并且在iOS9之后就不需要我们手动移除了。
最后在Father的dealloc方法中判断了一下:

- (void)dealloc {
    if ([self isMemberOfClass:[Father class]]) {
        [self removeObserver:self forKeyPath:@"testKVOKey"];
        [[NSNotificationCenter defaultCenter] removeObserver:self name:@"testNoti" object:nil];
    }
}

只在Father的dealloc中移除响应的KVO和通知,目前没有发现新的问题。这个问题当时排查了一阵没有头绪,其实仔细看控制台的打印就能发现,没有注册的类是子类而不是父类,最后定位到了问题点。

上一篇 下一篇

猜你喜欢

热点阅读