KVO addObserver逻辑在XCTest测试方法中崩溃



我在将键值观察逻辑与XCTest结合使用时遇到了一些困难(原始代码正在进行测试覆盖范围的改进)。逻辑在正常(非测试)上下文中运行良好,但每次在测试上下文中都会出现异常。

逻辑的要点是——我有两个类,称它们为Service和Helper。脚手架实施包括:

interface Service : NSObject {
BOOL svcCallComplete;
}
@end
@implementation Service
- (id) init {
if ((self=[super init])==nil) {
return nil;
}
return self;
}
@end
interface Helper : NSObject {
}
@end
@implementation Helper
- (id) init {
if ((self=[super init])==nil) {
return nil;
}
return self;
}
@end

Helper是Service中某个属性的观察者。在我的正常运行时逻辑的上下文中,我通过调用服务实例方法addSvcObserver:来实现这一点

服务.m:

- (void) addSvcObserver:(id)observer {
[self addObserver:observer
forKeyPath:@"svcCallComplete"
options:NSKeyValueObservingOptionNew
context:nil];
}

Helper符合KVO观察模式,因此:

Helper.m:

- (void) observeValueForKeyPath:(NSString*)keyPath
ofObject:(id)object
change:(NSDictionary*)change
context:(void*)context {
}

非常直接,我不会进入监控属性更改的逻辑,因为问题发生在之前——如果我有一个代码摘录,比如:

Service* service = [[Service alloc] init];
Helper* helper = [[Helper alloc] init]; 
[service addSvcObserver:helper];

在非测试情况下不存在问题(即该和相关的KVO逻辑按预期工作)。但是,当在XCTest测试方法的上下文中执行addSvcObserver调用时,会产生立即拒绝访问的异常。我包含了一个异常"break-on-all"断点——问题似乎发生在addObserver:forKeyPath:options:context:调用期间的objc_registerClassPair中。测试目标明确禁用了ARC,因为其提供测试覆盖的项目(目标为iOS7)由于遗留原因而非ARC;这似乎不会对其他测试造成任何问题。

想法?

interface Service : NSObject {
}
@property (nonatomic) BOOL svcCallComplete;

您应该将svcCallComplete声明为一个属性。

因为观察到的类必须与要观察的属性的键值观察兼容

我想你得到objc_registerClassPair的原因可能是KVO动态注册了Service的一个子类,但找不到svcCallComplete的setter方法。动态子类需要覆盖该setter方法并发送通知。

欲了解更多详细信息,请阅读本文。

原因是我对KVO逻辑的实现不完整。根据这里的指南,当使用手动更改通知时,必须覆盖automaticallyNotifiesObserversForKey:NSObject实现——我在最初阅读文本时不知何故错过了这一点。我在Service类中添加了以下内容:

+ (BOOL)automaticallyNotifiesObserversForKey:(NSString *)theKey {
BOOL automatic = NO;
if ([theKey isEqualToString:@"svcCallComplete"]) {
automatic = NO;
} else {
automatic = [super automaticallyNotifiesObserversForKey:theKey];
}
return automatic;
}

现在在测试用例中一切都是正确的。有人愿意冒险猜测为什么在正常(非测试)情况下没有爆发?

最新更新