我在使用键值观察逻辑与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中属性的观察者。在我的正常运行时逻辑的上下文中,我通过调用Service实例方法addSvcObserver
来执行此操作:
Service.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
调用会产生立即访问被拒绝的异常。
我已经包含了一个例外“中断所有”断点 - 问题似乎发生在objc_registerClassPair
addObserver:forKeyPath:options:context:
调用期间{{1}}。由于遗留原因,测试目标已明确禁用ARC,因为它提供测试覆盖的项目(目标是iOS7)是非ARC的;这似乎不会导致其他测试出现任何问题。
思想?
答案 0 :(得分:1)
interface Service : NSObject {
}
@property (nonatomic) BOOL svcCallComplete;
您应该将svcCallComplete声明为属性。
因为观察到的类必须符合您希望观察的属性的键值观察
你认为objc_registerClassPair
的原因可能是因为KVO动态注册了Service
的子类,但是找不到动态子类需要的svcCallComplete
的setter方法。覆盖该setter方法并发送通知。
更多细节read this。
答案 1 :(得分:0)
原因是我对KVO逻辑的实现不完整。根据指南here,当您使用手动更改通知时,必须覆盖NSObject
automaticallyNotifiesObserversForKey:
的{{1}}实现 - 我在初次阅读文本。我在Service
课程中添加了以下内容:
+ (BOOL)automaticallyNotifiesObserversForKey:(NSString *)theKey {
BOOL automatic = NO;
if ([theKey isEqualToString:@"svcCallComplete"]) {
automatic = NO;
} else {
automatic = [super automaticallyNotifiesObserversForKey:theKey];
}
return automatic;
}
现在测试用例中的所有内容都是正确的。任何人都在关注为什么在正常(非测试)情况下没有爆发的猜测?