我有一个包含循环方法的类。如果发生某个事件(例如按下按钮),我需要能够打破循环。
我在按下按钮时使用NSNotificationCenter
通知包含循环的类。
但是,如果我在执行循环时按下按钮,则会在循环完成后发出通知,而不是中断循环。
我猜这是因为它在同一个线程中运行。
那么如何在后台/不同的线程中运行NSNotificationCenter
?这可能吗?或者有更好的方法吗?
答案 0 :(得分:4)
这不仅仅是通知中心。
我有一个包含循环方法的类。如果发生某个事件(例如按下按钮),我需要能够打破循环。
按下按钮的事件进入主线程。如果您的循环在主线程上运行,那么按钮本身在循环完成之前不会被处理。相对于您的应用程序实际处理的按钮按钮,通知会立即发布。
或者,以列表形式:
您所看到的延迟是在步骤1和2之间;第4步发生在第3步之后。
本地(非分布式)NSNotificationCenter上的通知将在您发布的线程上发送,因此从您的操作方法发布它意味着它将在主线程上调度。这很正常,没问题。
将循环而不是通知移动到后台线程,调度队列或操作队列。如果使用操作队列,则可能根本不需要通知,因为您可以告诉操作队列取消所有挂起的操作。 (您的操作需要在任何适当的时间检查它们是否已被取消;对于reasons previously discussed,在随机时间杀死线程/操作是一个坏主意。)
后台线程,块和操作可以在需要时(例如,更新UI)与主线程通信。要通过主线程的运行循环发送消息,请使用performSelectorOnMainThread:withObject:waitUntilDone:
。要在主线程上调度块,请使用dispatch_async
和dispatch_get_main_queue
。要在主线程上安排操作,请将其添加到[NSOperationQueue mainQueue]
。
有关详情,请参阅Concurrency Programming Guide和Notification Programming Topics。
答案 1 :(得分:3)
我会在一个单独的线程中运行你的循环,并且有一个实例变量BOOL abort;
,当你按下按钮通知时,设置abort = TRUE;
然后在循环中检查这个值并退出,如果它是真。
答案 2 :(得分:3)
我会在一个单独的线程中运行循环。更好的是,将其设为NSOperation,以便您可以拨打[.. cancel]
。从NSOperation对象更新UI时,请确保使用performSelectorOnMainThread
。在主线程上运行一个长循环并不是一个好主意。
答案 3 :(得分:2)
您无法将通知中心放在另一个主题上。那个对象超出了你的控制范围。问题不在于它们与您不允许负责处理按钮按下的run loop执行任何操作的线程相同。 (每个线程只有一个运行循环。)正如edsko和Peter Hosey所说,按钮按下本身,实际上是整个UI,在循环运行时停止。通常一个好主意是将长时间运行的操作放到后台线程上,然后回调主线程来更新UI,performSelectorOnMainThread:withObject:waitUntilDone:
是一种简单的方法来进行这样的回调。
也就是说,如果要将循环保留在主线程上,则需要让控制定期返回到运行循环,以便按下按钮。我有两种方法可以做到这一点。首先,您可以在循环的每次迭代期间明确地给出运行循环控件:
while( !buttonWasPressed ){
// Do work...
// Let the run loop do some processing before the next iteration.
[[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.01]];
}
或者,您可以创建一个仅包含循环代码的方法,并使用performSelector:withObject:afterDelay:
重复调用该方法,同时仍允许运行循环工作:
- (void) loopTheLoop {
if( buttonWasPressed ) return;
// Do work...
// Run this method again as soon as possible.
[self performSelector:@selector(loopTheLoop)
withObject:nil
afterDelay:0.0];
}