进度开始后是否可以动态扩展NSProgress层次结构?

exo*_*nel 7 ios nsprogress

说我有一些NSProgress对象的层次结构.为简单起见,请说2个孩子的根进度.如果已经开始进度,我可以在层次结构中添加另一个子进程并期望正确的事情发生吗?例如,假设我开始与2个孩子,所以totalUnitCount根是2和pendingUnitCount是2.一旦第一个孩子完成,根本进展' fractionCompleted将是0.5.如果我然后添加另一个子进程,则根进度' fractionCompleted会~0.33按照我的意愿更新到,但是根目录totalUnitCount仍然保留2,以及pendingUnitCount.我可以totalUnitCount手动更新,但这会抛出root fractionCompleted.此外,似乎没有办法更新pendingUnitCount没有调用becomeCurrentWithPendingUnitCount:.是NSProgress不适合这种用途的或者是有一个"正确"的方式做到这一点?

我的用例是我有一个可以将文件上传到远程服务器的应用程序.用户可以选择一些文件并点击按钮开始上传它们.在他们上传时,用户可以看到上传作业的整体进度.现在,如果用户想要在上传已经进行时选择要上传的更多照片,我想更新进度以反映新的总数.例如,他目前正在上传2个文件,1个已经完成,当他们上传时,他还增加了2个上传文件.进展应该自我调整.现在总单位数应为4,并且还有3个项目要上传.对我来说这似乎是一个常见的用例.

这是我用动态扩展NSProgress层次结构编写的单元测试.下面测试中的所有断言都会通过,但是如果你检查根目录,totalUnitCount那么它总是保持为2,并且似乎没有办法告诉根目录进度还有多少项目需要完成.UI的进度是UI会观察到的,因此重要的是层次结构中更深层次的变化会传播,包括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);
}
Run Code Online (Sandbox Code Playgroud)

exo*_*nel 5

我向苹果公司提交了一个错误,这是他们的回应:

工程根据以下信息确定此问题的行为符合预期:

原因是,当“一组”孩子的任何成员(随着孩子的进步而在当前状态下随着孩子的进步而附于其上的那些成员)未完成时,我们无法更新其父母的完整单位数。组中的所有成员完成后,我们将该组与父级分离,并增加父级的已完成单位数。这样可以保留对已完成工作的正确核算。本地化的描述方法解决了这个问题,这就是为什么它给出与completedUnitCount属性不同的结果的原因。

如果要更紧密地跟踪这些子项的完成情况,则可以使父项更频繁地使用较小的单位数。然后,当孩子完成学习时,父母将更频繁地更新其完成的单元数。


exo*_*nel 1

好吧,我想我知道我做错了什么。在添加新子级之前,根必须完成正在进行的工作并调用 resignCurrent。然后,根可以更新totalUnitCount,并使用新的待处理单元计数再次成为Current,并且子级可以开始工作。看来currentProgress的completedUnitCount直到退出current才更新。这就是 localizedAdditionalDescription 错误的原因。不确定这是设计使然还是错误,但这意味着如果我要动态地将子节点添加到进度层次结构中,我就不能依赖根节点的completedUnitCount。

- (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
}
Run Code Online (Sandbox Code Playgroud)