我应该为我的UI操作使用后台线程吗? [英] Should I use a background thread for my UI actions?
问题描述
背景
-
我有一个
NSOutlineView
Training Group群组实体。 -
每个TrainingGroup代表本机上的资料夹。
-
NSOutlineView
绑定到NSTreeController
,并且fetch谓词IsTrained == 0
-
每个TrainingGroup可以分配给一个项目。
-
-
当TrainingGroup指定给某个项目时,
IsTrained
设置为
YES
。 -
分配给项目时,所有后代也分配到该项目,并且
IsTrained
属性也设置为YES
。 -
项目列绑定到
projectTopLevel
属性。
示例
整个树如下所示:
名称项目IsTrained
用户nil NO
John nil NO
文档nil NO
Acme项目Acme项目YES
提案。 doc Acme项目YES
12:32-12:33 Acme项目YES
13:11-13:33 Acme项目YES
... etc
Budget.xls Acme Project YES
Big Co项目Big Co项目YES
Deadlines.txt Big Co项目YES
Spec.doc Big Co项目YES
新项目nil NO
StartingUp.doc nil NO
个人资料个人YES
MyTreehouse.doc个人是
电影nil NO
Aliens.mov nil NO
StepMom.mov nil NO
而NSOutlineView只会看到:
用户nil NO
John nil NO
文档nil NO
新项目nil NO
StartingUp.doc nil NO
电影nil NO
外星人。 mov nil NO
StepMom.mov nil NO
如果您将Movies指定给Personal,现在看起来像这样:
用户nil NO
John nil NO
文档nil NO
新项目nil NO
StartingUp.doc nil NO
代码
TrainingGroup.m
- (void)setProjectTopLevel: JGProject *)projectToAssign {
[self setProjectForSelf:projectToAssign];
[self setProjectForChildren:projectToAssign];
}
- (void)setProjectForSelf:(JGProject *)projectToAssign {
[self setProject:projectToAssign];
}
- (void)setProjectForChildren:(JGProject *)projectToAssign {
for(TrainingGroup * thisTrainingGroup in [self descendants]){
[thisTrainingGroup setProject:projectToAssign ];
if(projectToAssign!= nil){
[thisTrainingGroup setIsTrainedValue:YES];
} else {
[thisTrainingGroup setIsTrainedValue:NO];
}
//其他代码更新规则。
}
}
- (JGProject *)projectTopLevel {
return [self project];
}
- (NSSet *)untrainedChildren {
//循环遍历所有返回
//的子节点的代码,其isTrained为NO。省略省略。
}
问题
当我在每个文件夹下有数百个时间条目时,我的应用程序
方法
- 在后台主题执行专案作业
- 完成后,使用标准Core Data合并到主上下文中。
- 模态工作表阻止任何进一步的活动,直到项目分配完成。
好
- 用户即时获得有关发生的反馈。
- 应用程式保持回应状态。
p> 2非模态微调器 好
b $ b
3隐藏
好与坏
- 同上,除了训练组的排序
好
- b $ b
- 据我理解,根据苹果的建议,密集处理不应
- 我可以获得足够的性能吗?未知。
我的问题
据我所见,的选项是理想的。
1。哪个是最好的选择?
2。还有其他选项吗?
3。
解决方案- 我会在背景上更新
- 您可以随时禁用窗口部分的用户输入,并显示加载消息。
- 遍历所有代码,并确保您所需的调用次数最少,并且不调用任何不必要的函数。您还可以在单独的线程上运行一些持久的操作,然后在操作完成时收到通知,以便在操作继续时处理其他内容。
Background
I've got an
NSOutlineView
that shows TrainingGroup entities.Each TrainingGroup represents a folder on the local machine.
The
NSOutlineView
is bound to anNSTreeController
with a fetch predicate ofIsTrained == 0
Each TrainingGroup can be assigned to a project.
Each TrainingGroup has many TrainingEntries that show a time worked on that file.
When the TrainingGroup is assigned to a project, the
IsTrained
is set toYES
.On assign to a project, all descendants are also assigned to that project and their
IsTrained
property is set toYES
too.Project column is bound to
projectTopLevel
property.
Example
The whole tree looks like this:
Name Project IsTrained Users nil NO John nil NO Documents nil NO Acme Project Acme Project YES Proposal.doc Acme Project YES 12:32-12:33 Acme Project YES 13:11-13:33 Acme Project YES ... etc Budget.xls Acme Project YES Big Co Project Big Co Project YES Deadlines.txt Big Co Project YES Spec.doc Big Co Project YES New Project nil NO StartingUp.doc nil NO Personal Stuff Personal YES MyTreehouse.doc Personal YES Movies nil NO Aliens.mov nil NO StepMom.mov nil NO
And the NSOutlineView would only see this:
Users nil NO John nil NO Documents nil NO New Project nil NO StartingUp.doc nil NO Movies nil NO Aliens.mov nil NO StepMom.mov nil NO
If you assigned Movies to Personal, the view would now look like this:
Users nil NO John nil NO Documents nil NO New Project nil NO StartingUp.doc nil NO
Code
TrainingGroup.m
-(void)setProjectTopLevel:(JGProject *)projectToAssign { [self setProjectForSelf:projectToAssign]; [self setProjectForChildren:projectToAssign]; } -(void)setProjectForSelf:(JGProject *)projectToAssign { [self setProject:projectToAssign]; } -(void)setProjectForChildren:(JGProject *)projectToAssign { for (TrainingGroup *thisTrainingGroup in [self descendants]) { [thisTrainingGroup setProject:projectToAssign]; if(projectToAssign != nil) { [thisTrainingGroup setIsTrainedValue:YES]; } else { [thisTrainingGroup setIsTrainedValue:NO]; } // Other code updating rules. } } -(JGProject *)projectTopLevel { return [self project]; } -(NSSet *)untrainedChildren { // Code that loops through all children returning those // whose isTrained is NO. Omitted for brevity. }
The Problem
As you can see above, I'm running all the project assignment code on the main thread currently.
When there are hundreds of time entries under each folder, my app becomes unresponsive.
Possible Solutions
1 Modal progress bar
The Approach
- Run project assignment on a background thread in separate context.
- Use standard Core Data merge into main context when finished.
- A modal sheet blocks any further activity until the project assignment has finished.
The Good
- The user gets immediate feedback on what's happening.
- The app remains responsive.
The Bad
- User can't do anything until the current assignment has finished.
2 Non Modal Spinner
The Approach
- Run project assignment on a background thread in separate context.
- Use standard Core Data merge into main context when finished.
- Show progress spinner alongside training group, indicating it's busy.
- On finish assigning, the training group disappears from the view.
The Good
- The user can do other stuff whilst their last action is being processed
- The app remains responsive. Kinda. See below.
The Bad
- In tests, I've seen a freeze of up to 3 seconds when the background context is merged into the main context.
- The view could update in the middle of the user doing something else, which might be annoying.
- Undo would be hard to implement.
3 Hide
The Approach
- Above, except the training group is removed on assign, and is set to be "In Progress" until assign has finished.
Good and Bad
- Same as above, except the ordering of the training groups would remain predicatable.
- Still large freeze on merge back into main context.
4 Improve performance
The Approach
- Keep the code as it is, running on the main thread.
- Improve performance so even with thousands of entries, the view only freezes for half a second max
The Good
- App remains responsive.
- Undo remains easy.
- Architecture remains simple.
The Bad
- As I understand it, against Apple's recommendations - intensive processing should not be done on the main thread
- Can I get the performance good enough? Unknown.
My Questions
As far as I can see, none of the options above are ideal.
1. Which is the best option?
2. Are there any other options?
3. What could I improve about my approach?
解决方案- I would update on a background thread (No. 2)
- You could always disable user input on sections of your window, and display a loading message.
- Simple to say - go through all your code, and make sure you are making the least number of calls required, and that you do not call any unnecessary functions. You could also run some long lasting operations on separate threads, and then get notified when the action finishes, so you can process other things while the operation goes on.
这篇关于我应该为我的UI操作使用后台线程吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!