- android - RelativeLayout 背景可绘制重叠内容
- android - 如何链接 cpufeatures lib 以获取 native android 库?
- java - OnItemClickListener 不起作用,但 OnLongItemClickListener 在自定义 ListView 中起作用
- java - Android 文件转字符串
我有一个包含多个子任务的大任务。我想为那个大任务添加进度报告。
为此,我想使用 NSProgress
,根据类文档,我可以通过使用它的子-父机制来完成这种子任务进度。
为了简化它,假设我有一个包含一个子任务的大任务(当然在现实生活中会有更多的子任务)。所以这就是我所做的:
#define kFractionCompletedKeyPath @"fractionCompleted"
- (void)runBigTask {
_progress = [NSProgress progressWithTotalUnitCount:100]; // 100 is arbitrary
[_progress addObserver:self
forKeyPath:kFractionCompletedKeyPath
options:NSKeyValueObservingOptionNew
context:NULL];
[_progress becomeCurrentWithPendingUnitCount:100];
[self subTask];
[_progress resignCurrent];
}
- (void)subTask {
NSManagedObjectContext *parentContext = self.managedObjectContext; // self is AppDelegate in this example
NSManagedObjectContext *bgContext = [[NSManagedObjectContext alloc]initWithConcurrencyType:NSPrivateQueueConcurrencyType];
[bgContext setParentContext:parentContext];
[bgContext performBlockAndWait:^{
NSInteger totalUnit = 1000;
NSInteger completedUnits = 0;
NSProgress *subProgress = [NSProgress progressWithTotalUnitCount:totalUnit];
for (int i=0; i < totalUnit; i++) {
// run some Core Data related code...
completedUnits++;
subProgress.completedUnitCount = completedUnits;
}
}];
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
if ([keyPath isEqualToString:kFractionCompletedKeyPath]) {
if ([object isKindOfClass:[NSProgress class]]) {
NSProgress *progress = (NSProgress *)object;
NSLog(@"progress… %f", progress.fractionCompleted);
}
} else {
[super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
}
}
可以看到,子任务使用后台上下文来运行一些Core Data相关的代码,后台上下文使用主上下文作为他的父上下文。
这会导致进度的“fractionCompleted”属性出现一些奇怪的 KVO。
这是打印品:
progress… 1.000000 // why???
progress… 0.500000 // why?????
progress… 1.000000 // why???????
progress… 0.666650 // why???????????
progress… 0.666990
progress… 0.667320
progress… 0.667660
progress… 0.667990
progress… 0.668320
...
progress… 1.000000
如您所见,打印从 1.0、0.5 和 1.0 开始,然后变为 0.66 ?!
从这里开始它正常运行并像我预期的那样变为 1.0。
我试图理解为什么会这样,我注意到如果我从后台上下文中删除父上下文,它就可以正常工作!我取得了从 0.0 到 1.0 的进步。
知道为什么会这样吗?我该如何解决?
我加了一个很 simple project演示此问题(您可以删除 setParentContext: 调用以查看它在没有它的情况下是否运行良好)
最佳答案
发生这种情况时的堆栈跟踪如下所示:
(lldb) bt
* thread #1: tid = 0x81f2, 0x0000000105bffcda Foundation`-[NSProgress setTotalUnitCount:], queue = 'com.apple.main-thread', stop reason = breakpoint 1.1
* frame #0: 0x0000000105bffcda Foundation`-[NSProgress setTotalUnitCount:]
frame #1: 0x0000000105bfeb1b Foundation`+[NSProgress progressWithTotalUnitCount:] + 87
frame #2: 0x0000000105a31213 Foundation`_NSReadBytesFromFileWithExtendedAttributes + 287
frame #3: 0x0000000105a3109d Foundation`-[NSData(NSData) initWithContentsOfFile:] + 89
frame #4: 0x0000000105a30b40 Foundation`+[NSDictionary(NSDictionary) newWithContentsOf:immutable:] + 101
frame #5: 0x0000000105a5622a Foundation`+[NSDictionary(NSDictionary) dictionaryWithContentsOfFile:] + 45
frame #6: 0x00000001043c4560 CoreData`-[NSManagedObjectModelBundle initWithPath:] + 224
frame #7: 0x00000001043c42ed CoreData`-[NSManagedObjectModel initWithContentsOfURL:] + 205
frame #8: 0x00000001040f723f CDProgress`-[AppDelegate managedObjectModel](self=0x00007fbe48c21f90, _cmd=0x000000010459b37b) + 223 at AppDelegate.m:127
frame #9: 0x00000001040f7384 CDProgress`-[AppDelegate persistentStoreCoordinator](self=0x00007fbe48c21f90, _cmd=0x000000010459c1cb) + 228 at AppDelegate.m:142
frame #10: 0x00000001040f708c CDProgress`-[AppDelegate managedObjectContext](self=0x00007fbe48c21f90, _cmd=0x0000000104598f0d) + 92 at AppDelegate.m:111
frame #11: 0x00000001040f6bdb CDProgress`-[AppDelegate subTask](self=0x00007fbe48c21f90, _cmd=0x00000001040f7997) + 43 at AppDelegate.m:45
frame #12: 0x00000001040f6b89 CDProgress`-[AppDelegate runTask](self=0x00007fbe48c21f90, _cmd=0x00000001040f7928) + 233 at AppDelegate.m:40
frame #13: 0x00000001040f6a4b CDProgress`-[AppDelegate application:didFinishLaunchingWithOptions:](self=0x00007fbe48c21f90, _cmd=0x0000000104f5dba9, application=0x00007fbe48f00fb0, launchOptions=0x0000000000000000) + 571 at AppDelegate.m:26
frame #14: 0x000000010477c5a5 UIKit`-[UIApplication _handleDelegateCallbacksWithOptions:isSuspended:restoreState:] + 234
frame #15: 0x000000010477d0ec UIKit`-[UIApplication _callInitializationDelegatesForMainScene:transitionContext:] + 2463
frame #16: 0x000000010477fe5c UIKit`-[UIApplication _runWithMainScene:transitionContext:completion:] + 1350
frame #17: 0x000000010477ed22 UIKit`-[UIApplication workspaceDidEndTransaction:] + 179
frame #18: 0x00000001088092a3 FrontBoardServices`__31-[FBSSerialQueue performAsync:]_block_invoke + 16
frame #19: 0x000000010615fabc CoreFoundation`__CFRUNLOOP_IS_CALLING_OUT_TO_A_BLOCK__ + 12
frame #20: 0x0000000106155805 CoreFoundation`__CFRunLoopDoBlocks + 341
frame #21: 0x00000001061555c5 CoreFoundation`__CFRunLoopRun + 2389
frame #22: 0x0000000106154a06 CoreFoundation`CFRunLoopRunSpecific + 470
frame #23: 0x000000010477e799 UIKit`-[UIApplication _run] + 413
frame #24: 0x0000000104781550 UIKit`UIApplicationMain + 1282
frame #25: 0x00000001040f7793 CDProgress`main(argc=1, argv=0x00007fff5bb09308) + 115 at main.m:16
frame #26: 0x000000010686f145 libdyld.dylib`start + 1
(lldb)
这里发生的事情是当模型被加载时,它正在读取一个 plist 文件。读取 plist 文件调用 -[NSData initWithContentsOfFile:]
,它在主线程上调用 +[NSProgress progressWithTotalUnitCount:]
。作为 release notes point out ,这将创建一个 NSProgress ,它是当前进度的子级。 initWithContentsOfFile:
实际上是这样做的,并为您创建的 NSProgress
创建一个新的子项:
<NSProgress: 0x7f9353596f80> : Parent: 0x0 / Fraction completed: 0.0000 / Completed: 0 of 1
<_NSProgressGroup: 0x7f935601a0d0> : Portion of parent: 100 Children: 1
<NSProgress: 0x7f935600bf50> : Parent: 0x7f9353596f80 / Fraction completed: 0.0000 / Completed: 0 of 0
这里发生的是额外的工作被添加到你的面前。此时,它对您要添加的额外工作一无所知。由 initWithContentsOfFile:
添加的子项完成,从树中删除,然后您开始添加您的工作。
当前进度从 0 开始,到 100%。您看到 100%,因为您的 KVO 选项不包括 NSKeyValueObservingOptionInitial
。
NSData
添加一个从 0 开始到 100% 的子进程。
您的 Core Data 任务添加了一个从 0 开始并(最终)达到 100% 的子项。
这里的一个关键点是您正在使用 performBlockAndWait:
。虽然 block 本身在私有(private)队列上运行,但此方法将阻塞调用线程,这将延迟您的 KVO 通知。 performBlockAndWait:
也将重用调用线程,如果可能的话,这是需要注意的事情。
如果你编辑你的 subTask
方法,用 NSProgress 包装自己作为整个工作单元的父级,最后退出当前,你可能会得到更接近你的行为期待:
- (void)subTask {
NSProgress *progress = [NSProgress progressWithTotalUnitCount:1];
NSManagedObjectContext *parentContext = self.managedObjectContext;
NSManagedObjectContext *bgContext = [[NSManagedObjectContext alloc]initWithConcurrencyType:NSPrivateQueueConcurrencyType];
[bgContext setParentContext:parentContext];
[progress becomeCurrentWithPendingUnitCount:1];
[bgContext performBlock:^{
... stuff
[progress resignCurrent];
}
NSProgress 可能有点难以理解,但有了一些经验就会变得更容易。我保证!
关于ios - NSProgress 奇怪的行为,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25870717/
我有这种来自 Google map 自动完成的奇怪行为(或者我可能错过了某事)...想法?奇怪的: 您在输入中输入某物,例如“伦敦” 您按 [ENTER] 你按下 [CLEAR] 按钮 你点击进入'输
这段代码与《Learning Java》(Oracle Press Books)一书中的代码完全一样,但它不起作用。我不明白为什么它不起作用,它应该起作用。我用 OpenJDK 和 Sun JDK 7
示例 1 中究竟发生了什么?这是如何解析的? # doesnt split on , [String]::Join(",",("aaaaa,aaaaa,aaaaa,aaaaa,aaaaa,aa
我需要获得方程式系统的解决方案。为此,我使用函数sgesv_()。 一切都很好,它使我感到解决方案的正确结果。 但是我得到一个奇怪的警告。 警告:从不兼容的指针类型传递'sgesv_'的参数3 我正在
我目前在制作动画时遇到一个奇怪的问题: [UIView animateWithDuration:3 delay:0
alert('works'); $(window).load(function () { alert('does not work'); });
我的代码: public class MyTest { public class StringSorter implements Comparator { public
我正在学习 JavaScript。尝试理解代码, function foo (){ var a = b = {name: 'Hai'}; document.write(a.name +''
这个问题不太可能帮助任何 future 的访问者;它只与一个小的地理区域、一个特定的时间点或一个非常狭窄的情况有关,这些情况并不普遍适用于互联网的全局受众。为了帮助使这个问题更广泛地适用,visit
这按预期工作: [dgorur@ted ~]$ env -i env [dgorur@ted ~]$ 这样做: [dgorur@ted ~]$ env -i which date which: no
struct BLA { int size_; int size()const{ return size_; } } int x; BLA b[ 2 ]; BLA * p = &b[
我有以下代码: #test img {vertical-align: middle;} div#test { border: 1px solid green; height: 150px; li
我想大多数使用过 C/C++ 的人都对预处理器的工作原理有一定的直觉(或多或少)。直到今天我也是这么认为的,但事实证明我的直觉是错误的。故事是这样的: 今天我尝试了一些东西,但我无法解释结果。首先考虑
我想为 TnSettings 做 mock,是的,如果通过以下方法编写代码,它就可以工作,问题是我们需要为每个案例编写 mock 代码,如果我们只 mock 一次然后执行多个案例,那么第二个将报告异常
我的项目中有以下两个结构 typedef volatile struct { unsigned char rx_buf[MAX_UART_BUF]; //Input buffer over U
Regex rx = new Regex(@"[+-]"); string[] substrings = rx.Split(expression); expression = "-9a3dcb
我的两个应用程序遇到了一个奇怪的问题。这是设置: 两个 tomcat/java 应用程序,在同一个网络中运行,连接到相同的 MS-SQL-Server。一个应用程序,恰好按顺序位于 DMZ 中可从互联
我目前正在与 Android Api Lvl 8 上的 OnLongClickListener 作斗争。 拿这段代码: this.webView.setOnLongClickListener(new
这个问题不太可能帮助任何 future 的访问者;它只与一个小的地理区域、一个特定的时间点或一个非常狭窄的情况相关,这些情况并不普遍适用于互联网的全局受众。为了帮助使这个问题更广泛地适用,visit
只是遇到了奇怪的事情。我有以下代码: -(void)ImageDownloadCompleat { [self performSelectorOnMainThread:@selector(up
我是一名优秀的程序员,十分优秀!