Access to user interface classes must take place in the main thread. Your accelerator does not work in the main thread.
Thus, it probably makes sense for your timer to light up in the main thread. You can use the standard libdispatch API even with pure C ++ code (it is not necessary to be .mm ObjC ++).
Be sure to add #include <dispatch/dispatch.h>
to your CPP implementation file.
The following code change ensures that the timer always runs in the main Cocoa thread.
void TimerImpl::TimerTick(const boost::system::error_code& e) { if(e != boost::asio::error::operation_aborted) { time_remaining_--; std:: cout << "C++: TimerTick() with " << std::to_string(time_remaining_) << " seconds remaining.\n"; if (time_remaining_ > 0) { timer_.expires_from_now(boost::posix_time::seconds(1)); timer_.async_wait(boost::bind(&TimerImpl::TimerTick, this, boost::asio::placeholders::error)); auto listener = listener_; auto time_remaining = time_remaining_; dispatch_async(dispatch_get_main_queue(), ^{ listener->TimerTicked(time_remaining); }); } else { auto listener = listener_; dispatch_async(dispatch_get_main_queue(), ^{ listener->TimerEnded(); }); } } }
I assume the rest of this code works. All I did was change the way callbacks are called. Note that we are creating a copy of the values โโof listener_
shared_ptr
and time_remaining_
. They will be captured (and copied) by a block that will be executed in the main thread.
If you can guarantee that this
will not be deleted before this block is executed, you can implicitly capture this
...
dispatch_async(dispatch_get_main_queue(), ^{ listener_->TimerTicked(time_remaining_); });
Or, if you enable shared-from-this, you can create a copy of the shared pointer to this
and commit it this way ...
auto self = shared_from_this(); dispatch_async(dispatch_get_main_queue(), ^{ self->listener_->TimerTicked(self->time_remaining_); });
There are several ways to do this, but it might be the easiest, and now you guarantee that all your timers will go through the main Cocoa thread.