IOS - performSelector:withObject:afterDelay: 未起作用

9

参考 :

https://dev59.com/d2oy5IYBdhLWcg3wJKvR#14741253

像上面的链接所说,但似乎没有解释原因。

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

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];
//    });

请问有人能告诉我为什么? 而且,“self”永远不会释放/解除分配,我会一直保留它直到应用程序结束。

“不起作用”表示(没有崩溃),它不会跳转到“helloWorld”方法:

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

我认为是运行循环引起了这个问题。就像这个链接所说的那样,但我需要更多的细节或更明确的解释。

1
如果您确实在主线程上,那就很奇怪了。为什么不使用 dispatch_after() 呢?是否有特别的原因呢? - user557219
1
“doesn't work”是什么意思?它会崩溃吗?如果是的话,日志信息是什么? - meronix
2
看到你最后的编辑,似乎你不是在主线程上,这种情况下@WaltSellers的回答适用。你如何测试自己是否在主线程上?你能否编写一个小的测试用例来重现你的问题? - user557219
你有一个运行循环吗?你是从应用程序委托中调用它的吗?self是什么? - Daij-Djan
performSelector 调用之前添加以下代码:NSLog(@"%@ Main thread", [NSThread isMainThread] ? @"ON" : @"NOT ON");,以查看您是否正在主线程上运行。 - Abizern
显示剩余8条评论
3个回答

25

当我遇到这种情况时,我正在从GCD调度中调用performSelector。 因此,它在GCD工作线程中设置定时器,但该线程在定时器触发之前消失了。 当GCD删除工作线程时,定时器丢失,因此选择器从未被调用。


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,因为如果你将块存储在实例变量中,可能会导致循环引用,从而导致对象无法被释放。 在这里他们谈论了这个问题:如何在实现 API 时避免在块中捕获 self?

一个对已释放对象的选择器调用通常会导致崩溃,对吗? - Walt Sellers
7
我相信performSelector方法也会保留所有内容。 - Walt Sellers
我也像W.Sellers一样认为,performSelector成为感兴趣的对象的所有者,保留它直到执行选择器,因此在此期间不需要担心self被释放。 - meronix

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];
});

在此线程中,使用[NSRunLoop currentRunLoop]返回PO
<CFRunLoop 0x6040001ea000 [0x10c642960]>
{
 wakeup port   = 0x5207, 
 stopped       = false, 
 ignoreWakeUps = true, 
 current mode  = (none),.....
}

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

但是如果 performSelector:withObject:afterDelay:Main Thread 中,例如在 viewDidLoad 中,该函数将被调用。

以下日志是在 viewDidLoad 中使用的 [NSRunLoop currentRunLoop]

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

@isaacselement


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接