0

有可能performSelector:withObject:afterDelay:在子线程中不起作用吗?

我对 Objective c 和 Xcode 还是很陌生,所以也许我错过了一些明显的东西......:/我真的很感激一些帮助。

我要做的就是显示一个信息标签 3 秒钟,然后将其隐藏。如果设置了新信息,则应取消在 3 秒后隐藏标签的线程。(我不希望通过旧线程隐藏新信息。)

源代码:

- (void) setInfoLabel: (NSString*) labelText
{
   // ... update label with text ...

    infoLabel.hidden = NO;

    if(appDelegate.infoThread != nil) [appDelegate.infoThread cancel]; // cancel last hide-thread, if it exists

    NSThread *newThread = [[NSThread alloc] initWithTarget: self selector:@selector(setInfoLabelTimer) object: nil];// create new thread
    appDelegate.infoThread = newThread; // save reference
    [newThread start]; // start thread


    [self performSelector:@selector(testY) withObject: nil afterDelay:1.0];

}


-(void) setInfoLabelTimer
{
    NSLog(@"setInfoLabelTimer");


    [self performSelector:@selector(testX) withObject: nil afterDelay:1.0];

    [self performSelector:@selector(hideInfoLabel) withObject: nil afterDelay:3.0];

    NSLog(@"Done?");
}

-(void) testX
{
 NSLog(@"testX testX testX testX testX");

}

-(void) testY
{
    NSLog(@"testY testY testY testY testY");

}

-(void) hideInfoLabel
{
    NSLog(@"f hideInfoLabel");
    if(!([[NSThread currentThread] isCancelled])) {
        AppDelegate *appDelegate = (AppDelegate *) [[UIApplication sharedApplication] delegate];
        appDelegate.infoThread = nil;
        appDelegate.infoLabel.hidden = YES;
        [NSThread exit];
    }
}

控制台输出:

  • 设置信息标签定时器
  • 完毕?
  • 测试Y 测试Y 测试Y 测试Y 测试Y

正如您所看到的那样performSelector:withObject:afterDelay:工作(--->“testY testY testY testY testY”),但不在子线程中(运行(--->“setInfoLabelTimer”和“Done?”))

有谁知道为什么performSelector:withObject:afterDelay:在子线程中不起作用?(或者我的错是什么?:()

最好的问候,茶壶

4

4 回答 4

1

顺便说一句,您可能要考虑使用Grand Central Dispatch,GCD。如果你想在三秒钟内完成某件事,你可以:

double delayInSeconds = 3.0;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
    // do stuff here, and because it's in the main queue, you can do UI stuff, too
});

我还建议您参阅并发编程指南中的Migrating Away From Threads 。


或者,您可以使用动画块,而不是使用 GCD,您可以在其中指定您希望在 3.0 秒内发生什么。您还可以为该过渡设置动画(在我的示例中为 0.25 秒),以便移除控件更加优雅:

[UIView animateWithDuration:0.25
                      delay:3.0
                    options:0
                 animations:^{
                     // you can, for example, visually hide in gracefully over a 0.25 second span of time

                     infoLabel.alpha = 0.0;
                 }
                 completion:^(BOOL finished) {
                     // if you wanted to actually remove the view when the animation was done, you could do that here

                     [infoLabel removeFromSuperview];
                 }];
于 2013-06-25T13:29:40.587 回答
1

如果您正在运行“子”线程(不是主线程的线程),它可以以两种方式之一运行:

  1. 它运行一个方法,然后终止
  2. 它运行一个运行循环并处理队列中的项目

如果线程以形式 1 运行,您使用performSelector将项目放入队列(或至少尝试)但它永远不会得到处理,线程将终止。

如果你想performSelector在线程上使用,你需要做额外的工作。或者,您可以将项目推送到正在运行运行循环的主线程上。

于 2013-06-25T13:30:40.677 回答
1

如果要在线程上调用 performSelector:withObject:afterDelay,则该线程必须有一个正在运行的 RunLoop。查看 Apple 的线程编程指南。这里也是 RunLoop 和 NSThread 的示例

您可以在 setInfoLabelTimer 中添加以下代码:

while (!self.isCancelled)
{
    [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode
                                            beforeDate:[NSDate distantFuture]];
}
于 2013-06-25T14:04:33.137 回答
0

根本不需要线程或 GCD 来做你想做的事。

只需performSelector:withObject:afterDelay:直接在主线程上使用,我们可以使用 @Rob 指示的动画,dispatch_after在主队列上使用,或者NSTimer.

于 2013-06-25T15:46:20.337 回答