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

时间:2013-12-31 00:08:45

标签: objective-c key-value-observing 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中属性的观察者。在我的正常运行时逻辑的上下文中,我通过调用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的;这似乎不会导致其他测试出现任何问题。

思想?

2 个答案:

答案 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;
}

现在测试用例中的所有内容都是正确的。任何人都在关注为什么在正常(非测试)情况下没有爆发的​​猜测?