我有一个包含带有循环的方法的类。如果发生某个事件(例如按下按钮),我需要能够打破循环。
我正在使用 NSNotificationCenter 在按下按钮时通知包含循环的类。
但是,如果我在执行循环时按下按钮,则循环完成后会出现通知,而不是中断循环。
我猜这是因为它在同一个线程中运行。
那么如何让 NSNotificationCenter 在后台/不同线程中运行呢?这可能吗?或者有更好的方法吗?
I have a class containing a method with a loop. I need to be able to break the loop if a certain event (e.g. button press) occurs.
I am using the NSNotificationCenter
to notify the class containing the loop when the button is pressed.
However, If I press the button while the loop is being executed, the notification occurs after the loop is complete instead of interrupting the loop.
I'm guessing this is because it is operating in the same thread.
So how do I get the NSNotificationCenter
operating in a background / different thread? Is this possible? Or is there a better way to do it?
发布评论
评论(4)
这不仅仅是通知中心。
该按钮按下的事件进入主线程。如果您的循环在主线程上运行,则在循环完成之前按下按钮本身不会得到处理。相对于应用程序实际处理的按钮按下,通知会立即发布。
或者,以列表形式:
您看到的延迟介于步骤 1 和步骤 2 之间;步骤 4 紧随步骤 3 之后发生。
本地(非分布式)NSNotificationCenter 上的通知将在您发布通知的线程上分派,因此从操作方法发布它意味着它将在主线程上分派。这是正常现象,没问题。
将循环(而不是通知)移至后台线程、调度队列或操作队列。如果您使用操作队列,则可能根本不需要通知,因为您可以告诉操作队列取消所有挂起的操作。 (您的操作需要在任何适当的时间检查它们是否已被取消;对于 之前讨论过的原因,随机终止线程/操作是一个坏主意。)
背景线程、块和操作可以在需要时(例如,更新 UI)与主线程通信。要通过主线程的运行循环发送消息,请使用
performSelectorOnMainThread:withObject:waitUntilDone:
。要在主线程上调度块,请使用dispatch_async和dispatch_get_main_queue。要在主线程上安排操作,请将其添加到[NSOperationQueue mainQueue]
。有关详细信息,请阅读并发编程指南和通知编程主题。
It's not just the notification center.
The events for that button press come in on the main thread. If your loop is running on the main thread, then the button press itself does not get processed until your loop is finished. The notification is posted immediately, relative to the button press actually getting processed by your application.
Or, in list form:
The delay that you're seeing is between steps 1 and 2; step 4 happens immediately after step 3.
Notifications on a local (not distributed) NSNotificationCenter are dispatched on the thread you post them from, so posting it from your action method means that it will be dispatched on the main thread. This is normal and OK.
Move the loop, not the notification, to a background thread, dispatch queue, or operation queue. If you use an operation queue, you may not need the notification at all, as you can tell an operation queue to cancel all pending operations. (Your operations will need to check at any appropriate time(s) whether they have been canceled; for reasons previously discussed, killing a thread/operation at a random time is a Bad Idea.)
Background threads, blocks, and operations can communicate back to the main thread when needed (e.g., to update the UI). To send a message through the main thread's run loop, use
performSelectorOnMainThread:withObject:waitUntilDone:
. To dispatch a block on the main thread, usedispatch_async
anddispatch_get_main_queue
. To schedule an operation on the main thread, add it to[NSOperationQueue mainQueue]
.For more info, read the Concurrency Programming Guide and the Notification Programming Topics.
我会在一个单独的线程中运行你的循环,并有一个实例变量
BOOL abort;
,当你的按钮按下通知进来时,设置abort = TRUE;
然后在循环中检查该值,如果为真则退出。I would run your loop in a separate thread, and have an instance variable
BOOL abort;
, when your button press notification comes in, setabort = TRUE;
then in the loop check this value and exit if it is true.我会在单独的线程中运行循环。更好的是,将其设为 NSOperation,以便您可以调用
[.. cancel]
。只需确保在从 NSOperation 对象更新 UI 时使用performSelectorOnMainThread
即可。在主线程上运行长时间循环并不是一个好主意。I would run the loop in a separate thread. Even better, make it an NSOperation so that you can call
[.. cancel]
. Just make sure to useperformSelectorOnMainThread
when updating the UI from the NSOperation object. It's not a good idea to have a long running loop on the main thread.您不能将通知中心放在另一个线程上。该物体不受你的控制。问题不在于它们位于同一线程上,而在于您不允许 runloop,它负责处理按钮按下,做任何事情。 (每个线程有一个且只有一个运行循环。)正如 edsko 和 Peter Hosey 所说,按下按钮本身,实际上整个 UI,在循环运行时都会停止。通常,最好将长时间运行的操作放在后台线程上,然后回调到主线程来更新 UI,
performSelectorOnMainThread:withObject:waitUntilDone:
是执行此类回调的简单方法。也就是说,如果要将循环保留在主线程上,则需要让控制定期返回到运行循环,以便记录按钮按下情况。我可以想到两种方法来做到这一点。首先,您可以在循环的每次迭代期间显式地短暂提供运行循环控制:
或者,您可以创建一个仅包含循环中的代码的方法,并使用
performSelector:withObject:afterDelay:
重复调用该方法仍然允许运行循环工作:You can't put the notification center on another thread. That object is out of your control. The problem isn't so much that they are on the same thread as that you are not allowing the run loop, which is responsible for handling the button press, to do anything. (There's one and only one run loop per thread.) As has been stated by both edsko and Peter Hosey, the button press itself, and in fact your entire UI, is stopped while your loop is running. It is generally a good idea to put long-running operations onto a background thread, then call back to the main thread to update the UI,
performSelectorOnMainThread:withObject:waitUntilDone:
being an easy way to do such a call back.That said, if you were to keep the loop on the main thread, you need to let control return to the run loop periodically so that the button press will be registered. There are two ways I can think of to do this. First, you can explicitly give the run loop control briefly during each iteration of your loop:
Or, you can make a single method that consists only of the code from your loop, and use
performSelector:withObject:afterDelay:
to have the method repeatedly called while still allowing the run loop to work: