【发布时间】:2018-02-28 08:14:18
【问题描述】:
我正在库中实现NSProgress 支持,并且我编写了一些单元测试来测试一切是否正常工作。虽然理想情况下我希望能够传递一些额外的元数据(userInfo 本身不被NSProgress 使用的键,但供我的API 的用户使用),现在我只是想获得localizedDescription 和localizedAdditionalDescription 像文档所说的那样工作。由于我正在测试的方法从存档中提取文件,因此我将kind 设置为NSProgressKindFile 并设置与文件操作相关的各种键(例如NSProgressFileCompletedCountKey)。
我希望当我观察到使用 KVO 对 localizedDescription 的更改时,我会看到如下更新:
处理“测试文件 A.txt”
处理“测试文件 B.jpg”
处理“测试文件 C.m4a”
当我停在断点处,polocalizedDescription 在工作人员NSProgress 实例(下面的childProgress)上,这实际上就是我所看到的。但是当我的测试运行时,他们看到的只是以下内容,这意味着它没有看到我设置的任何 userInfo 键:
0% 完成
0% 完成
53% 完成
100% 完成
100% 完成
看起来我在子 NSProgress 实例上设置的 userInfo 键没有传递给它的父实例,尽管 fractionCompleted 确实如此。我做错了吗?
我在下面给出了一些抽象代码sn-ps,但您也可以下载带有这些更改的提交from 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
{
}
【问题讨论】:
-
你能显示更多的上下文代码吗?调用 setUserInfoObject 后控制台中的 po myCustomObject、po MyCustomKey 和 po [childProgress userInfo] 立即显示什么?
-
@clarus 我添加了
po输出。对于上下文,您还需要哪些其他代码?与NSProgress相关的内容不多。fractionCompleted得到准确报告。 -
observeValueForKeyPath:ofObject:change:context: 中的代码是什么样的?而且,您在上面使用 parentProgress 的地方,是否与原始 childProgress 对象引用相同?
-
@clarus 感谢您的建议。我更新了更详尽的代码示例。它应该有望解决您的问题。
-
@clarus 随着我对此进行了更多的研究,看起来甚至记录在案的
userInfo键也没有按预期工作。我已经更新了问题,并包含了一个指向 GitHub 提交的链接,您可以下载并运行。
标签: ios objective-c macos cocoa nsprogress