我有一个由几个子任务组成的大任务.我想为这项重大任务添加进度报告.
为此,我想使用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%.

您的核心数据任务会添加一个从0开始并且(最终)达到100%的子项.

然而,这里的一个关键点是你正在使用performBlockAndWait:.虽然块本身在专用队列上运行,但此方法将阻止调用线程,这将延迟您的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奇怪的行为的更多相关文章

  1. 浅谈HTML5 FileReader分布读取文件以及其方法简介

    本篇文章主要介绍了浅谈HTML5 FileReader分布读取文件以及其方法简介,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

  2. ios – 声明NSDictionary并在Swift中添加键值对?

    我一直在尝试使用类类型键和值来声明一个NSDictionary,如下所示:这里,“Category”和“SubCategory”是全局类.我知道我不能将类类型用于关键字段.但是,无论如何,我应该做到这一点.有没有办法做到这一点?如何声明专门的NSDictionary或类似的东西来做到这一点?

  3. ios – Swift相当于`[NSDictionary initWithObjects:forKeys:]`

    Swift的原生字典是否与[NSDictionaryinitWithObjects:forKeys:]相当?假设我有两个带键和值的数组,并希望将它们放在字典中.在Objective-C中,我这样做:当然我可以通过两个数组迭代一个计数器,使用vardict:[String:Int]并逐步添加东西.但这似乎不是一个好的解决方案.使用zip和enumerate可能是同时迭代两者的更好方法.然而,这种方法

  4. ios – NSJSONSerialization崩溃的应用程序

    我有一本字典,当我记录它时显示…我正试图将它变成NSData,用于像这样的JSONWeb服务……但每次我运行它时,应用程序都会崩溃.字典正确形成,应用程序只是崩溃在这一行.在AppCode中,我收到崩溃报告……在Xcode中,应用程序停止,如果我尝试继续,它会因错误而停止…

  5. xcode – 错误“线程1:断点2.1”

    我正在研究RESTAPI管理器.这是一个错误,我无法解决它.我得到的错误在下面突出显示.当我打电话给这个班级获取资源时:我评论的线打印:Thread1:breakpoint2.1我需要修复错误的建议.任何建议都非常感谢解决方法您可能在不注意的情况下意外设置了断点.单击并拖动代表断路器外部断点的蓝色刻度线以将其擦除.

  6. ios – Apple Watch,WatchKit Extension和主要应用程序

    有逻辑的主要应用程序,我们将应用程序扩展到AppleWatch.添加目标xCode后,再创建2个应用程序:扩展代码和监视工具包应用程序.问题:扩展程序中的代码如何重用已准备好的主要iOS应用程序的逻辑?

  7. ios – 如果Element符合给定的协议,则扩展阵列以符合协议

    如果是这样,语法是什么?解决方法Swift4.2在Swift4.2中,我能够使用符合这样的协议的元素扩展数组:

  8. 通过AFNetworking 2.0上传iOS图像

    我一直在寻找新的AFNetworking2.0上传图像的例子.但是我正在撞墙,无法弄清楚代码有什么问题.所以这是我使用的代码TIA解决方法我最终使用了多部分请求

  9. ios – 使用drawInRect时如何设置文本颜色?

    我一直在使用PaintCode在我的应用程序中绘制一些图形,一些绘制的元素具有使用“drawInRect”绘制的文本.无论我做什么尝试并设置颜色,文本总是显示为黑色.这是创建文本的代码,我试图设置颜色:“文本绘图”中的白色是不能正常工作的.谢谢你的帮助.解决方法您需要通过以下方式设置传入的“属性”字典中文本的颜色:我的建议是:

  10. xcode – 尝试在Pin上居中地图(MKMapView)

    苦苦寻找一种方法来使地图缩放并以注释引脚为中心.Pin下降,但地图加载海洋.代码如下.第2个问题,非常相关:在实现上述问题的答案后,我已经修改了我的代码.现在,我的坐标从前一个视图到我的MKMapView,所以我不必费心去做两次API调用,第二个是在MKMapView中.目前在我的ViewWillAppear中,我有以下内容,并且AGAIN遇到了一个问题,即视图不会居中并放大图钉:反馈非常感谢,因为我不知道还应该做些什么.引脚加载到正确的坐标上,只是没有居中/缩放…

随机推荐

  1. iOS实现拖拽View跟随手指浮动效果

    这篇文章主要为大家详细介绍了iOS实现拖拽View跟随手指浮动,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

  2. iOS – genstrings:无法连接到输出目录en.lproj

    使用我桌面上的项目文件夹,我启动终端输入:cd然后将我的项目文件夹拖到终端,它给了我路径.然后我将这行代码粘贴到终端中找.-name*.m|xargsgenstrings-oen.lproj我在终端中收到此错误消息:genstrings:无法连接到输出目录en.lproj它多次打印这行,然后说我的项目是一个目录的路径?没有.strings文件.对我做错了什么的想法?

  3. iOS 7 UIButtonBarItem图像没有色调

    如何确保按钮图标采用全局色调?解决方法只是想将其转换为根注释,以便为“回答”复选标记提供更好的上下文,并提供更好的格式.我能想出这个!

  4. ios – 在自定义相机层的AVFoundation中自动对焦和自动曝光

    为AVFoundation定制图层相机创建精确的自动对焦和曝光的最佳方法是什么?

  5. ios – Xcode找不到Alamofire,错误:没有这样的模块’Alamofire’

    我正在尝试按照github(https://github.com/Alamofire/Alamofire#cocoapods)指令将Alamofire包含在我的Swift项目中.我创建了一个新项目,导航到项目目录并运行此命令sudogeminstallcocoapods.然后我面临以下错误:搜索后我设法通过运行此命令安装cocoapodssudogeminstall-n/usr/local/bin

  6. ios – 在没有iPhone6s或更新的情况下测试ARKit

    我在决定下载Xcode9之前.我想玩新的框架–ARKit.我知道要用ARKit运行app我需要一个带有A9芯片或更新版本的设备.不幸的是我有一个较旧的.我的问题是已经下载了新Xcode的人.在我的情况下有可能运行ARKit应用程序吗?那个或其他任何模拟器?任何想法或我将不得不购买新设备?解决方法任何iOS11设备都可以使用ARKit,但是具有高质量AR体验的全球跟踪功能需要使用A9或更高版本处理器的设备.使用iOS11测试版更新您的设备是必要的.

  7. 将iOS应用移植到Android

    我们制作了一个具有2000个目标c类的退出大型iOS应用程序.我想知道有一个最佳实践指南将其移植到Android?此外,由于我们的应用程序大量使用UINavigation和UIView控制器,我想知道在Android上有类似的模型和实现.谢谢到目前为止,guenter解决方法老实说,我认为你正在计划的只是制作难以维护的糟糕代码.我意识到这听起来像很多工作,但从长远来看它会更容易,我只是将应用程序的概念“移植”到android并从头开始编写.

  8. ios – 在Swift中覆盖Objective C类方法

    我是Swift的初学者,我正在尝试在Swift项目中使用JSONModel.我想从JSONModel覆盖方法keyMapper,但我没有找到如何覆盖模型类中的Objective-C类方法.该方法的签名是:我怎样才能做到这一点?解决方法您可以像覆盖实例方法一样执行此操作,但使用class关键字除外:

  9. ios – 在WKWebView中获取链接URL

    我想在WKWebView中获取tapped链接的url.链接采用自定义格式,可触发应用中的某些操作.例如HTTP://我的网站/帮助#深层链接对讲.我这样使用KVO:这在第一次点击链接时效果很好.但是,如果我连续两次点击相同的链接,它将不报告链接点击.是否有解决方法来解决这个问题,以便我可以检测每个点击并获取链接?任何关于这个的指针都会很棒!解决方法像这样更改addobserver在observeValue函数中,您可以获得两个值

  10. ios – 在Swift的UIView中找到UILabel

    我正在尝试在我的UIViewControllers的超级视图中找到我的UILabels.这是我的代码:这是在Objective-C中推荐的方式,但是在Swift中我只得到UIViews和CALayer.我肯定在提供给这个方法的视图中有UILabel.我错过了什么?我的UIViewController中的调用:解决方法使用函数式编程概念可以更轻松地实现这一目标.

返回
顶部