线程和autoreleasepool问题

时间:2011-09-17 06:25:20

标签: iphone objective-c nsthread nsautoreleasepool performselector

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

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”并且应该避免?每次启动模态视图时,我都会在仪器中收到一些奇怪的内存泄漏错误。但它来自其中一个“系统库”,我被告知这是非常难以调试的。这可能会出现什么问题?

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

1 个答案:

答案 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之类的内容很有用。