Можно ли динамически расширять иерархию NSProgress после начала прогресса?

Скажем, у меня есть некоторая иерархия NSProgress объекты. Для простоты скажем основной прогресс с 2 детьми. Если прогресс уже начался, могу ли я добавить еще один дочерний прогресс в иерархию и ожидать, что это произойдет правильно? Например, скажем, я начинаю с 2 детьми, поэтому totalUnitCount корень 2 и pendingUnitCount является 2, Как только первый ребенок завершит корень прогресса " fractionCompleted будет 0.5, Если я потом добавлю еще одного ребенка fractionCompleted обновляет до ~0.33 как хотелось бы но рут totalUnitCount остается в 2как и pendingUnitCount, Я могу обновить totalUnitCount вручную, но это бросает корень fractionCompleted выкл. Кроме того, кажется, нет возможности обновить pendingUnitCount без звонка becomeCurrentWithPendingUnitCount:, Было NSProgress не предназначен для такого использования или есть "правильный" способ сделать это?

Мой пример использования - у меня есть приложение, которое может загружать файлы на удаленный сервер. Пользователь может выбрать некоторые файлы и нажать кнопку, чтобы начать их загрузку. Во время загрузки пользователь видит общий ход выполнения задания загрузки. Теперь, если пользователь хочет выбрать еще несколько фотографий для загрузки, когда загрузки уже выполняются, я хочу обновить прогресс, чтобы отразить новый итог. Например, в настоящее время он загружает 2 файла, 1 из которых уже завершен, и пока они загружают, он добавляет еще 2 для загрузки. Прогресс должен наладиться сам. Общее количество единиц теперь должно быть 4, и для загрузки осталось 3 элемента. Похоже, это было бы обычным делом для меня.

Вот модульный тест, который я написал для динамического расширения NSProgress иерархия. Все утверждения в тесте ниже проходят, но если вы проверите рут totalUnitCount оно всегда остается равным 2, и, похоже, нет никакого способа определить, сколько элементов осталось выполнить из основного процесса. Корневой прогресс - это тот, который будет наблюдать пользовательский интерфейс, поэтому важно, чтобы изменения в иерархии распространялись глубже, включая totalUnitCount, так далее.

- (void)testDynamicNSProgress
{
    NSProgress *root = [NSProgress progressWithTotalUnitCount:2];
    [root becomeCurrentWithPendingUnitCount:2];

    NSProgress *child1 = [NSProgress progressWithTotalUnitCount:1];
    NSProgress *child2 = [NSProgress progressWithTotalUnitCount:1];
    XCTAssertEqual(root.totalUnitCount, 2);
    XCTAssertEqual(root.completedUnitCount, 0);

    child1.completedUnitCount++;
    XCTAssertEqual(ceil(root.fractionCompleted * 100), 50);

    NSProgress *child3 = [NSProgress progressWithTotalUnitCount:1];
    NSProgress *child4 = [NSProgress progressWithTotalUnitCount:1];
    XCTAssertEqual(ceil(root.fractionCompleted * 100), 25);

    child2.completedUnitCount++;
    XCTAssertEqual(ceil(root.fractionCompleted * 100), 50);

    child3.completedUnitCount++;
    XCTAssertEqual(ceil(root.fractionCompleted * 100), 75);

    child4.completedUnitCount++;
    XCTAssertEqual(ceil(root.fractionCompleted * 100), 100);

    __unused NSProgress *child5 = [NSProgress progressWithTotalUnitCount:1];
    XCTAssertEqual(ceil(root.fractionCompleted * 100), 80);
}

2 ответа

Я подал ошибку в Apple, и это был их ответ:

Инжиниринг установил, что эта проблема ведет себя как задумано, основываясь на следующей информации:

Причина в том, что мы не можем обновить счетчик завершенных единиц родителя, пока какой-либо член "группы" потомков (тех, кто присоединен к нему по мере продвижения ребенка, пока идет ход) не завершен. Как только все члены группы будут готовы, мы отсоединяем эту группу от родителя и увеличиваем количество завершенных единиц родителя. Это сохраняет правильный учет выполненных работ. Это объясняется методом локализованного описания, поэтому он дает результат, отличный от свойства completeUnitCount.

Если вы хотите более внимательно отслеживать завершение этих потомков, то вы можете сделать так, чтобы родитель стал более актуальным с меньшим количеством единиц. Затем, когда дочерние элементы будут завершены, родитель будет чаще обновлять счетчик завершенных единиц.

Ладно, думаю, я знаю, что я делаю не так. Перед добавлением нового дочернего элемента корень должен завершить текущую работу и вызвать resignCurrent. Затем корень может обновить totalUnitCount, снова стать Current с новым количеством ожидающих единиц, и дети могут выполнять работу. Кажется, что completeUnitCount currentProgress не обновляется до тех пор, пока не уйдет в отставку current. Вот почему localizedAdditionalDescription неверно. Не уверен, что это по замыслу или это ошибка, но это означает, что я не могу полагаться на completeUnitCount корневого узла, если я собираюсь динамически добавлять дочерние элементы в иерархию прогресса.

- (void)testDynamicNSProgress2
{
    NSProgress *root = [NSProgress progressWithTotalUnitCount:2];
    [root becomeCurrentWithPendingUnitCount:2];

    XCTAssertEqual(root.completedUnitCount, 0);
    NSLog(@"%@", root.localizedDescription);           // 0% completed
    NSLog(@"%@", root.localizedAdditionalDescription); // 0 of 2

    // Add two children
    NSProgress *child1 = [NSProgress progressWithTotalUnitCount:1];
    NSProgress *child2 = [NSProgress progressWithTotalUnitCount:1];

    // First child completes work    
    child1.completedUnitCount++;
    XCTAssertEqual(root.completedUnitCount, 0);
    NSLog(@"%@", root.localizedDescription);           // 50% complete
    NSLog(@"%@", root.localizedAdditionalDescription); // 1 of 2

    // Second child completes work
    child2.completedUnitCount++;
    [root resignCurrent];
    XCTAssertEqual(root.completedUnitCount, 2);
    NSLog(@"%@", root.localizedDescription);           // 100% complete
    NSLog(@"%@", root.localizedAdditionalDescription); // 2 of 2

    // Update totalUnitCount and become current again
    root.totalUnitCount = 3;
    [root becomeCurrentWithPendingUnitCount:1];
    XCTAssertEqual(root.completedUnitCount, 2);
    XCTAssertEqual(root.totalUnitCount, 3);
    NSLog(@"%@", root.localizedDescription);           // 66% completed
    NSLog(@"%@", root.localizedAdditionalDescription); // 1 of 3 (wrong! should be 2 of 3)

    // Last child completes
    NSProgress *child3 = [NSProgress progressWithTotalUnitCount:1];
    child3.completedUnitCount++;
    [root resignCurrent];
    XCTAssertEqual(root.completedUnitCount, 3);
    NSLog(@"%@", root.localizedDescription);           // 100% completed
    NSLog(@"%@", root.localizedAdditionalDescription); // 3 of 3
}
Другие вопросы по тегам