我正在一个库中实现NSProgress支持,我编写了一些单元测试来测试一切是否正常。虽然理想情况下,我希望能够传递一些额外的元数据(userInfo键不是由NSProgress本身使用,而是供我的API用户使用),但就目前而言,我只是想让localizedDescription和localizedAdditionalDescription像文档所说的那样工作。由于我正在测试的方法从存档中提取文件,所以我将kind设置为NSProgressKindFile,并设置与文件操作相关联的各种键(例如NSProgressFileCompletedCountKey)。
当我观察到KVO对localizedDescription的更改时,我会看到这样的更新:
处理“测试文件A.txt” 处理“测试文件B.jpg” 处理“测试文件C.m4a”
当我停在一个断点上,并在worker po实例(下面的childProgress)上设置localizedDescription时,这实际上就是我所看到的。但是,当我的测试运行时,他们看到的只是下面的内容,这意味着它没有看到我设置的任何userInfo键:
0%已完成 0%已完成 53%已完成 100%完成 100%完成
看起来,我在子userInfo NSProgress 实例上设置的键并没有传递给它的父实例,尽管 fractionCompleted 是这样做的。我做错什么了吗?
下面我给出了一些抽象代码片段,但您也可以通过这些更改来自GitHub下载提交。如果您想要重现这种行为,请运行-[ProgressReportingTests testProgressReporting_ExtractFiles_Description]和-[ProgressReportingTests testProgressReporting_ExtractFiles_AdditionalDescription]测试用例。
在我的测试用例类中:
static void *ProgressContext = &ProgressContext;
...
- (void)testProgressReporting {
NSProgress *parentProgress = [NSProgress progressWithTotalUnitCount:1];
[parentProgress becomeCurrentWithPendingUnitCount:1];
[parentProgress addObserver:self
forKeyPath:NSStringFromSelector(@selector(localizedDescription))
options:NSKeyValueObservingOptionInitial
context:ProgressContext];
MyAPIClass *apiObject = // initialize
[apiObject doLongRunningThing];
[parentProgress resignCurrent];
[parentProgress removeObserver:self
forKeyPath:NSStringFromSelector(@selector(localizedDescription))];
}
- (void)observeValueForKeyPath:(NSString *)keyPath
ofObject:(id)object
change:(NSDictionary<NSKeyValueChangeKey,id> *)change
context:(void *)context
{
if (context == ProgressContext) {
// Should refer to parentProgress from above
NSProgress *notificationProgress = object;
[self.descriptionArray addObject:notificationProgress.localizedDescription];
}
}然后,在我被测试的班级里:
- (void) doLongRunningThing {
...
NSProgress *childProgress = [NSProgress progressWithTotalUnitCount:/* bytes calculated above */];
progress.kind = NSProgressKindFile;
[childProgress setUserInfoObject:@0
forKey:NSProgressFileCompletedCountKey];
[childProgress setUserInfoObject:@(/*array count from above*/)
forKey:NSProgressFileTotalCountKey];
int counter = 0;
for /* Long-running loop */ {
[childProgress setUserInfoObject: // a file URL
forKey:NSProgressFileURLKey];
// Do stuff
[childProgress setUserInfoObject:@(++counter)
forKey:NSProgressFileCompletedCountKey];
childProgress.completedUnitCount += myIncrement;
}
}在我增加childProgress.completedUnitCount时,这就是调试器中userInfo的样子。我设置的字段都表示为:
> po childProgress.userInfo
{
NSProgressFileCompletedCountKey = 2,
NSProgressFileTotalCountKey = 3,
NSProgressFileURLKey = "file:///...Test%20File%20B.jpg"; // chunk elided from URL
}当每个KVO通知返回时,notificationProgress.userInfo的外观如下:
> po notificationProgress.userInfo
{
}发布于 2017-09-22 20:23:09
我想对@clarus的回答发表评论,但不会让我在评论中做可读的格式化。TL;DR -他们的观点一直是我的理解,在几年前我开始与NSProgress合作时,这就是我的痛处。
对于这种情况,我喜欢检查Swift基金会代码以获得实现提示。如果事情还没有完成的话,这也许并不是百分之百的权威,但是我喜欢看到一般的想法。
如果您查看setUserInfoObject(: forKey:)的实现,您可以看到该实现只是设置用户信息dict,而没有将任何内容传播到父节点。
相反,影响子部分已完成的显式回叫到(私有) _parent属性以指示其状态的更新应响应子级更改。
私有_updateChild(: from: to: portion:)似乎只关心更新已完成的部分,而不是任何与用户信息字典相关的内容。
https://stackoverflow.com/questions/46302206
复制相似问题