9

参考 :

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
}

我认为是Run Loop导致了这个问题。就像这个链接说的那样,但我需要更多细节或更明确的解释。

4

3 回答 3

25

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

于 2013-06-06T08:17:10.920 回答
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 中,您可能最终会出现一个保留循环,这会导致对象不会被释放。他们在这里谈论这个问题: 在实现 API 时,如何避免在块中捕获自我?

于 2013-06-06T08:05:51.063 回答
0

苹果文件说:

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:是 in Main Thread,例如 in viewDidLoad,该函数将被调用。

以下日志[NSRunLoop currentRunLoop]位于viewDidLoad

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

@isaacselement

于 2018-04-16T09:03:41.117 回答