IOS - performSelector:withObject:afterDelay:NOT WORKING

时间:2013-06-06 07:38:14

标签: iphone ios objective-c

参考:

https://stackoverflow.com/a/14741253/1749293

就像上面说的链接一样,但似乎并没有解释原因。

在我的代码中,以下内容将起作用:

dispatch_async(dispatch_get_main_queue(), ^{
       [self performSelector:  @selector(helloWorld) withObject:nil afterDelay:0.5];
}); 

但是,当我评论这样的事情时,(我确信我在主线程中运行它!!)代码不起作用:

//    dispatch_async(dispatch_get_main_queue(), ^{
        [self performSelector:  @selector(helloWorld) withObject:nil afterDelay: 0.5];
//    });

有人可以告诉我为什么吗? AND 'self',将神经释放/解除分配,我保留它直到应用程序结束。

“不工作”,意味着,(没有崩溃)它不会跳转到“helloWorld”方法:

-(void) helloWorld {
    NSLog(@"hello world");     // I set a break point here for debug , it wouldn't pause forever
}

我认为运行循环会导致此问题。就像this link所说,但我需要更多细节或更明确的解释。

3 个答案:

答案 0 :(得分:23)

当我发生这种事情时,我从GCD调度中调用performSelector。所以它是在GCD工作线程中设置定时器,它在定时器触发之前就消失了。当GCD删除了工作线程时,计时器丢失了,因此从不调用选择器。

答案 1 :(得分:1)

修改 正如评论中所述,performSelector:withObject:afterDelay:也保留了你的对象,所以忽略我的答案。 结束编辑

我认为你正在使用ARC。你的块正在保留你的对象。

dispatch_async(dispatch_get_main_queue(), ^{
       [self performSelector:  @selector(helloWorld) withObject:nil afterDelay:aTimeUnit];
});

这就是激发选择器的原因。当您对块进行注释时,没有人保留对您的对象的引用,因此它会自动释放。

//    dispatch_async(dispatch_get_main_queue(), ^{
        [self performSelector:  @selector(helloWorld) withObject:nil afterDelay: aTimeUnit];
//    });

aTimeUnit已经过去时,self可能已被释放,因此选择器调用将丢失。那是你的问题。

你应该避免在一个块中捕获self,因为如果你将块存储在一个ivar中,你可能会得到一个保留周期,这会导致对象不被释放。他们在这里谈论: How do I avoid capturing self in blocks when implementing an API?

答案 2 :(得分:0)

Apple Documents说:

This method sets up a timer to perform the aSelector message on the current thread’s run loop.

The timer is configured to run in the default mode (NSDefaultRunLoopMode).

It succeeds if the run loop is running and in the default mode; otherwise, the timer waits until the run loop is in the default mode

请注意: 仅在默认模式下成功(NSDefaultRunLoopMode)

现在。假设你有这样的代码:

dispatch_async(dispatch_queue_create("com.serial.thread", DISPATCH_QUEUE_SERIAL), ^{
    NSLog(@"1");
    [self performSelector:@selector(testLog2) withObject:nil afterDelay:0];
});

PO[NSRunLoop currentRunLoop]中的Thread

<CFRunLoop 0x6040001ea000 [0x10c642960]>
{
 wakeup port   = 0x5207, 
 stopped       = false, 
 ignoreWakeUps = true, 
 current mode  = (none),.....
}

正如您所看到的,current mode(none)。所以永远不会调用该函数!

但如果performSelector:withObject:afterDelay:位于Main Thread,例如viewDidLoad,则会调用该函数。

[NSRunLoop currentRunLoop]中的以下日志为viewDidLoad

<CFRunLoop 0x6000001e9700 [0x10c642960]>
{
 wakeup port   = 0x1d03, 
 stopped       = false, 
 ignoreWakeUps = false, 
 current mode  = kCFRunLoopDefaultMode,......
}

@isaacselement