2

我在结合 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

服务.m:

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

Helper 遵循 KVO 观察模式,因此:

助手.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 逻辑按预期工作)。但是,addSvcObserver在 XCTest 测试方法的上下文中执行调用时会立即产生拒绝访问异常。我已经包含了一个异常“全部中断”断点——问题似乎发生objc_registerClassPairaddObserver:forKeyPath:options:context:。测试目标明确禁用了 ARC,因为它提供测试覆盖的项目(目标是 iOS7)由于遗留原因是非 ARC;这似乎不会对其他测试造成任何问题。

想法?

4

2 回答 2

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

您应该将 svcCallComplete 声明为属性。

因为对于您希望观察的属性,观察到的类必须符合键值观察

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

有关更多详细信息,请阅读此

于 2013-12-31T01:17:38.680 回答
0

造成这种情况的原因原来是我对 KVO 逻辑的实现不完整。根据此处的指南,您必须在使用手动更改通知时覆盖的NSObject实现automaticallyNotifiesObserversForKey:——我在最初阅读文本时不知何故错过了这一点。我在Service课堂上添加了以下内容:

+ (BOOL)automaticallyNotifiesObserversForKey:(NSString *)theKey {

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

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

于 2013-12-31T03:34:07.800 回答