即使从主线程调用更新,UI 也不会更新
Posted
技术标签:
【中文标题】即使从主线程调用更新,UI 也不会更新【英文标题】:UI not updating even though updates are called from main thread 【发布时间】:2012-03-02 15:18:11 【问题描述】:我已经阅读了我能找到的所有相关问题,但我仍然卡住了,所以我希望有人能发现我的推理错误。
我正在尝试定期更新一些 UIView。为简单起见,我将代码简化为以下内容。摘要:在 viewDidLoad 中,我在一个新的后台线程上调用了一个方法。该方法调用主线程上的一个方法,该方法应该更新一些 UILabel。代码似乎工作正常:后台线程不是主线程,调用 UILabel 更新的方法在主线程上。在代码中:
在 viewDidLoad 中:
[self performSelectorInBackground:@selector(updateMeters) withObject:self];
这会创建一个新的后台线程。我的方法 updateMeters(为简单起见)现在看起来像这样:
if ([NSThread isMainThread]) //this evaluates to FALSE, as it's supposed to
NSLog(@"Running on main, that's wrong!");
while (i < 10)
[self performSelectorOnMainThread:@selector(updateUI) withObject:nil waitUntilDone:NO];
//The code below yields the same result
// dispatch_async(dispatch_get_main_queue(), ^
// [self updateUI];
// );
[NSThread sleepForTimeInterval: 1.05];
++i;
最后,updateUI 就是这样做的:
if ([NSThread isMainThread]) //Evaluates to TRUE; it's indeed on the main thread!
NSLog(@"main thread!");
else
NSLog(@"not main thread!");
NSLog(@"%f", someTimeDependentValue); //logs the value I want to update to the screen
label.text = [NSString stringWithFormat:@"%f", someTimeDependentValue]; //does not update
据我所知,这应该可行。但不幸的是,它没有......注释掉的dispatch_async()
产生相同的结果。
【问题讨论】:
什么是“someTimeDependentValue”?我想是一个浮点数.. 你试过使用 NSTimer 吗?也许在 viewDidLoad 中,你启动了一个 NSTimer。在勾选时,让它执行您的 UI 更新。在单个视图中使用 2 个独立的自引用进程有点令人困惑。 @Jeremy 你的意思是这样的吗?[self performSelectorInBackground:@selector(updateMeters) withObject:self];
NSInvocation *invoc = [NSInvocation invocationWithMethodSignature:[self methodSignatureForSelector:@selector(updateUI)]];
[NSTimer timerWithTimeInterval:1. invocation:invoc repeats:YES];
我不熟悉 NSInvocation 对象,所以我不确定这是正确的。使用此代码,不会调用 updateUI。
我没有得到它应该更新的内容,这就是价值。你有没有想过可能 someTimeDependentValue 没有被评估?使用 NSlog 或断点检查它是否确实有一些值。
@Andrea 对不起。为了简洁起见,我删除了一个 NSLog() 语句。 someTimeDependentValue 确实得到了更新。
【参考方案1】:
很可能您的格式说明有误。
label.text = [NSString stringWithFormat:@"%f", someTimeDependentValue];
确保 someTimeDependentValue 是一个浮点数。如果它是一个 int,它可能会被格式化为 0.0000。
Here's a repo 显示您所描述的工作版本。任何错误都与线程无关。
【讨论】:
换句话说,您是说我在问题中列出的代码应该可以工作? 它在那个仓库中工作。我几乎只是将您的代码复制并粘贴到 UIViewController 的子类中。我遇到的唯一障碍是我最初将 tdv(我创建的用于替换 someTimeDependentValue 的实例变量)声明为 int,因为我没有注意到您的 stringWithFormat 要求浮点/双精度。 这很奇怪。我在方法中也有一个NSLog()
,它正在打印(更新)值。所以我很确定的一件事是,格式声明是正确的,我提供的值也是正确的。我会更新我的问题以反映这一点。
非常感谢您的回购!我应该能够使用它找到我的错误!
嗯,插座肯定有问题...当我将引用从弱更改为强时,我的方法中返回了 EXC_BAD_ACCESS,但直接在 ViewDidLoad 中除外。我试图通过以编程方式创建标签来隔离问题,但这会产生相同的错误。我真的很困惑(并且非常害怕我错过了一些非常明显的东西)......【参考方案2】:
为了扩展我的评论,这里有一个使用 NSTimer 可能最好实现的场景:
-(void)viewDidLoad
NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:<number of seconds per tick> target:self selector:@selector(timerTick:) userInfo:nil repeats:YES];
-(void)timerTick:(id)sender
label.text = ...;
我在我的项目中广泛使用了一种更精细的方法。这就是引擎的概念。
我会有一个使用计时器在后台运行的引擎。在关键时刻,它会使用 dispatch_async
/dispatch_get_main_thread()
在主线程上发布使用 NSNotificationCenter
的通知,然后您的任何一个视图都可以通过更新其 UI 来订阅和处理该通知。
【讨论】:
以上是关于即使从主线程调用更新,UI 也不会更新的主要内容,如果未能解决你的问题,请参考以下文章