据我所知,有几种方法可以发送要在线程中执行的任务。我使用的最常见的是:
1)performSelector:withObject:afterDelay:
2)performSelectorOnMainThread:withObject:waitUntilDone:
3)performSelectorInBackground:withObject:
4)[NSThread detachNewThreadSelector:toTarget:withObject:]
我的第一个问题是,1)和2)之间有什么区别,除了明显的参数差异?它们实际上是否都在主线程中工作(其自动释放池是在main.m中自动创建的)?我刚从Stackoverflow上某人的帖子中读到,方法1)实际上是在新线程中工作,因此应该为其选择器方法创建自动释放池。它是否正确?我一直在使用1),主要是利用延迟参数,但我从来没有为它们创建自动释放池。没有发生任何灾难性事件。
接下来,3)和4)都在一个单独的线程中执行任务。我听说UI的东西永远不应该在这些线程中完成,但我对什么是严格的UI感到困惑。 我正在尝试编写代码来基本播放重复的加载动画,而tableview是从navigationcontroller以模态方式启动的。然后在tableview控制器的viewDidLoad方法中停止动画。最初,我只是将代码卡在启动模态视图的代码行之上。发生了什么事是动画从未播放过。
[[self loadingView] playAnimation];
SettingsViewController *menus = [[SettingsViewController alloc] initWithNibName:@"SettingsViewController" bundle:nil];
MyNavigationController *navController = [[MyNavigationController alloc] initWithRootViewController:menus];
[menus setParent:navController];
[navController setDelegate:self];
menus.mainViewController = self;
[self presentModalViewController:navController animated:YES];
[navController release];
[menus release];
然后我尝试了以下内容,它有效......
[NSThread detachNewThreadSelector:@selector(settingsOpeningThread) toTarget:self withObject:nil];
[[self loadingView] playAnimation];
- (void) settingsOpeningThread {
NSAutoreleasePool *apool = [[NSAutoreleasePool alloc] init];
SettingsViewController *menus = [[SettingsViewController alloc] initWithNibName:@"SettingsViewController" bundle:nil];
MyNavigationController *navController = [[MyNavigationController alloc] initWithRootViewController:menus];
[menus setParent:navController];
[navController setDelegate:self];
menus.mainViewController = self;
[self presentModalViewController:navController animated:YES];
[navController release];
[menus release];
[apool release];
}
动画一直播放,直到完全启动SettingsViewController视图。但启动这样的模态视图是否算作“UI”并且应该避免?每次启动模态视图时,我都会在仪器中收到一些奇怪的内存泄漏错误。但它来自其中一个“系统库”,我被告知这是非常难以调试的。这可能会出现什么问题?
对于令人尴尬的长篇帖子感到抱歉。任何帮助将不胜感激!
答案 0 :(得分:1)
(1)为当前runloop 安排任务。在非常高的层次上,UIKit应用程序看起来像
while(true) {
update UI
run all tasks that were scheduled last time through the loop
}
这就是您第一次尝试未更新UI的原因;对playAnimation
的调用会调度UI在runloop的下一次迭代中更新,但是在完成后面的代码之前它永远不会到达那里。
请注意,performSelector:withObject:afterDelay
不在单独的线程中运行指定的代码。
(2)做了一些非常相似的事情,但是不是为当前的runloop安排一些东西,而是为主线程上的runloop安排一些东西。这仅在从单独的线程调用时才有用,通常是因为您想要从辅助线程更新UI。
是的,你的代码略显大胆。我建议做类似的事情:
[[self loadingView] playAnimation];
[self performSelector:@selector(loadTable) withObject:nil afterDelay:0]
加载表的实际代码位于loadTable
。这意味着当runloop到来时,你的UI将被更新,动画开始播放,然后调用loadTable
方法并完成它的工作。
但是,如果动画需要主线程的干预来执行,那么这仍然无效。也就是说,如果加载表的代码会停止主线程,那么您的动画也可能会停止。除了在单独的线程中执行长期运行任务(可能会或可能不会使用performSelector:onMainThread:waitUntilDone
在主线程上安排UI更新)之外,实际上没有办法解决这个问题。
如果您对动画本身并不太在意,可能会发现https://github.com/samvermette/SVProgressHUD之类的内容很有用。