如何让我的应用程序响应更快?
背景
我有一个显示 TrainingGroup 实体的
NSOutlineView
。NSOutlineView
绑定到NSTreeController
- < p>在 NSTreeController 中,我勾选了“保留选择”,未勾选“选择插入的对象”。
每个 TrainingGroup 代表本地计算机上的一个文件夹。
每个 TrainingGroup 都可以分配给一个项目。该项目应传播到该组的所有后代。
项目列与每个培训组的项目属性绑定。
此视图中有大量数据。因为每次条目都有一个条目,所以在一个训练视图下总共可以有大约 15000 个后代。
大纲视图
树如下所示:
Name Project
Users nil
John nil
Documents nil
Acme Project Acme Project
Proposal.doc Acme Project
12:32-12:33 Acme Project
13:11-13:33 Acme Project
... thousands more here!
Budget.xls Acme Project
Big Co Project Big Co Project
Deadlines.txt Big Co Project
Spec.doc Big Co Project
New Project nil
StartingUp.doc nil
Personal Stuff Personal
MyTreehouse.doc Personal
Movies nil
Aliens.mov nil
StepMom.mov nil
在项目分配时
编辑项目时,对所有子项的分配发生在
NSOperation
上后台线程上的子类,以便用户可以自由地进行其他选择并在处理所有后代时四处移动。操作完成后,我在主托管对象上下文上运行
mergeChangesFromContextDidSaveNotification:
方法:mainContext = [[NSApp delegate] ManagedObjectContext];
[mainContext PerformSelectorOnMainThread:@selector(mergeChangesFromContextDidSaveNotification:) withObject:通知 waitUntilDone:YES];
从功能上来说,这工作得很好 - 当上下文合并回 main 时,所有后代都被分配给项目。
问题
合并时,绑定到主上下文的
NSOutlineView
会冻结,并且合并可能需要几秒钟才能完成。为了减少这种冻结,我已将组分批分配给几个较小的操作。
这种批量后台操作方法存在三个问题:
界面在不到一秒的时间内变得无响应。这不是什么大问题,但它确实意味着界面中会出现不可预测的小暂停。
大纲视图更新合并回的每个批次。执行此操作时,选择可能会出现问题。
指定组下的某些项目仍为空白。该对象已分配给项目,但大纲视图尚未正确刷新项目。大概是因为主线程被合并中断了。
您可以在我录制的截屏视频中看到这些问题:
替代方案
我可以选择的 将每个操作的更改合并到在后台线程上运行的托管对象上下文中。也许我可以向主线程发送一条消息来刷新顶级组中已更改的所有对象。
这可能会解决上面的问题 1 和 3,但我认为 2 仍然是一个问题。
我的问题
有更好的方法吗?
在我开始重新设计如何执行此操作之前(我已经更改过一次它的工作方式)我想知道是否有更好的方法或有任何方法我不知道这种方法的缺点。
预先感谢您对替代方案的任何建议。
Background
This is the same background as my previous question, except the Outline view doesn't have a fetch predicate.
I've got an
NSOutlineView
that shows TrainingGroup entities.The
NSOutlineView
is bound to anNSTreeController
In the NSTreeController, I've got "Preserve Selection" ticked and "Select inserted objects" unticked.
Each TrainingGroup represents a folder on the local machine.
Each TrainingGroup can be assigned to a project. The project should propagate to all descendants of that group.
The project column is bound to the project property of each training group.
There's a lot of data in this view. Because each time entry has an entry, there can be a total of ~15000 descendants under one training view.
Outline View
The tree looks like this:
Name Project
Users nil
John nil
Documents nil
Acme Project Acme Project
Proposal.doc Acme Project
12:32-12:33 Acme Project
13:11-13:33 Acme Project
... thousands more here!
Budget.xls Acme Project
Big Co Project Big Co Project
Deadlines.txt Big Co Project
Spec.doc Big Co Project
New Project nil
StartingUp.doc nil
Personal Stuff Personal
MyTreehouse.doc Personal
Movies nil
Aliens.mov nil
StepMom.mov nil
On Project Assign
When a project is edited, the assignment to all children happens on an
NSOperation
subclass on a background thread so the user is free to make other selections and move around whilst all descendants are processed.When the operation has finished, I run the
mergeChangesFromContextDidSaveNotification:
method on the main managed object context:mainContext = [[NSApp delegate] managedObjectContext];
[mainContext performSelectorOnMainThread:@selector(mergeChangesFromContextDidSaveNotification:)
withObject:notification
waitUntilDone:YES];Functionally, this works fine - all descendants are assigned to the project when the context is merged back into main.
The Problem
On merge, the
NSOutlineView
that's bound to the main context freezes and the merge can take several seconds to complete.To reduce this freeze, I've batched up the groups to assign into several smaller operations.
There are three problems with this batched background operation approach:
The interface becomes unresponsive for a fraction of a second. This isn't such a big deal, but it does mean small unpredictable pauses in the interface.
The outline view updates each batch that's merged back in. When it does this, the selection can be glitchy.
Some projects under assigned groups remain blank. The object has been assigned the project, but the outline view hasn't refreshed the project properly. Presumably because the main thread is interrupted by the merges.
You can see these issues in the screencast I recorded:
The Alternative
I could merge the changes from each operation into a managed object context running on a background thread. Them maybe I could send a message to the main thread to refresh all objects within the top level group that was changed.
That might get rid of problems 1 and 3 above, but I think 2 would still be an issue.
My Question
Is there a better way of doing this?
Before I start reworking how I do this (I've already changed how this works once) I want to know if there's a better way or there are any drawbacks to this approach I don't know about.
Thanks in advance for any suggestions on alternatives.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
我目前正生活在冰冻之中。但我认为答案是并行我的 NSOperations 并减少将合并所需的时间降至最低。毫无疑问,我可以对数据模型进行很多改进,以使操作也更容易合并。
我最近尝试在后台线程上实现合并,但很难使其工作。我想这将是我重新审视的事情。
I'm currently living with the freeze. But I think the answer is to parallel up my NSOperations and just reduce the time taken to make the merge to the minimum possible. There are doubtless lots of improvements I can make to the data model to make the operations much easier to merge in too.
I tried to implement a merge on background thread recently, but struggled to make it work. It'll be something I revisit, I think.