Наблюдение за ключом-значением NSOperation выполняет старые и новые значения не так, как ожидалось

#ios #objective-c #key-value-observing #nsoperation

#iOS #objective-c #наблюдение за значением ключа #nsoperation

Вопрос:

При наблюдении за isExecuted свойством NSOperation объекта я вижу некоторое неожиданное поведение. Я регистрирую наблюдателя с параметрами NSKeyValueObservingOptionNew и. NSKeyValueObservingOptionOld Я бы ожидал, что последнее наблюдение изменит словарь для isExecuting , чтобы иметь значение @YES для NSKeyValueChangeNewKey и @NO для NSKeyValueChangeOldKey . Тем не менее, я вижу, что эти значения меняются местами. Все остальные наблюдения выполняются так, как ожидалось. Я делаю что-то не так или, возможно, в SDK есть ошибка? Я использую Xcode 8 с iOS SDK версии 10.0, а целевая платформа моего проекта — 8.0.

Ниже приведен неудачный случай XCTest, чтобы проиллюстрировать неожиданное поведение, которое я вижу.

 #import <XCTest/XCTest.h>

@interface TestNSOperationKVO : XCTestCase

@end

@implementation TestNSOperationKVO {
    NSMutableArray<NSDictionary *> *changes;
}

NSString *kKeyPath = @"keyPath";
NSString *kChange = @"change";

- (void)setUp {
    [super setUp];

    changes = [NSMutableArray array];
}

- (void)tearDown {
    [super tearDown];
}

- (void)testIsExecutingKVO {
    NSOperation *op = [NSBlockOperation blockOperationWithBlock:^{}];
    [op addObserver:self forKeyPath:NSStringFromSelector(@selector(isExecuting)) options:(NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld) context:NULL];
    [op addObserver:self forKeyPath:NSStringFromSelector(@selector(isFinished)) options:(NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld) context:NULL];

    [op start];

    XCTAssertEqual(changes.count, 3);
    XCTAssert([@"isExecuting" isEqualToString:changes[0][kKeyPath]]);
    XCTAssertEqual(changes[0][kChange][NSKeyValueChangeOldKey], @NO);
    XCTAssertEqual(changes[0][kChange][NSKeyValueChangeNewKey], @YES);
    XCTAssert([@"isExecuting" isEqualToString:changes[1][kKeyPath]]);
    // this fails
    XCTAssertEqual(changes[1][kChange][NSKeyValueChangeOldKey], @YES);
    // this fails
    XCTAssertEqual(changes[1][kChange][NSKeyValueChangeNewKey], @NO);
    XCTAssert([@"isFinished" isEqualToString:changes[2][kKeyPath]]);
    XCTAssertEqual(changes[2][kChange][NSKeyValueChangeOldKey], @NO);
    XCTAssertEqual(changes[2][kChange][NSKeyValueChangeNewKey], @YES);
}

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context
{
    [changes addObject:@{kKeyPath: keyPath, kChange: change}];
}

@end