1

据我了解,有几种方法可以发送要在线程中执行的任务。我最常用的是:

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 控制器的 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”并且应该避免?此外,每次启动模式视图时,我都会在 Instruments 中遇到一些奇怪的内存泄漏错误。但它来自那些“系统库”之一,我被告知很难调试。这里可能出了什么问题?

很抱歉这篇令人尴尬的长帖子。任何帮助将不胜感激!

4

1 回答 1

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这样的东西很有用。

于 2011-10-10T09:05:55.417 回答