【发布时间】:2015-11-11 17:21:07
【问题描述】:
当通过双向 djinni 架构从 C++ 进行 UI 调用时,我在 Xcode 7.1 中收到以下错误:
This application is modifying the autolayout engine from a background thread, which can lead to engine corruption and weird crashes. This will cause an exception in a future release.
我可以用这里给出的解决方案来解决 Objective-C 中的问题:
Getting a “This application is modifying the autolayout engine” error?
dispatch_async(dispatch_get_main_queue(), ^{
// code here
});
我的问题是,有没有一种方法可以在 C++ 中以某种方式完成此任务,而不必在每次调用 UI 时都在 Objective-C 中调用 dispatch_async?还是就 Xcode 而言,来自 C++ 的每个调用都被视为后台线程?
发布相关代码,省略自动生成的源文件,完整项目也可在github:
cpptimer.djinni:
timer = interface +c {
static create_with_listener(listener: timer_listener): timer;
start_timer(seconds: i32);
}
timer_listener = interface +j +o {
timer_ticked(seconds_remaining: i32);
timer_ended();
}
timer_impl.hpp
#pragma once
#include <boost/asio.hpp>
#include "timer.hpp"
#include "timer_listener.hpp"
namespace cpptimer {
class TimerImpl : public Timer {
public:
TimerImpl(const std::shared_ptr<TimerListener> & listener);
void StartTimer(int32_t seconds);
private:
void TimerTick(const boost::system::error_code& e);
std::shared_ptr<TimerListener> listener_;
boost::asio::io_service io_service_;
boost::asio::deadline_timer timer_;
int time_remaining_;
};
}
timer_impl.cpp
#include <boost/bind.hpp>
#include <boost/thread.hpp>
#include "timer_impl.hpp"
namespace cpptimer {
std::shared_ptr<Timer> Timer::CreateWithListener(const std::shared_ptr<TimerListener> & listener) {
return std::make_shared<TimerImpl>(listener);
}
TimerImpl::TimerImpl(const std::shared_ptr<TimerListener> & listener):
io_service_(),
timer_(io_service_, boost::posix_time::seconds(1)) {
listener_ = listener;
}
void TimerImpl::StartTimer(int32_t seconds) {
time_remaining_ = seconds;
io_service_.reset();
timer_.async_wait(boost::bind(&TimerImpl::TimerTick, this, boost::asio::placeholders::error));
boost::thread th([&] { io_service_.run(); });
}
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));
listener_->TimerTicked(time_remaining_);
} else {
listener_->TimerEnded();
}
}
}
}
ViewController.h
#import <UIKit/UIKit.h>
#import "CPPTTimerListener.h"
@interface ViewController : UIViewController<CPPTTimerListener>
@property (nonatomic, strong) IBOutlet UILabel *timerLabel;
@end
ViewController.m
#import "ViewController.h"
#import "CPPTTimer.h"
@interface ViewController () {
CPPTTimer *_timer;
}
@end
@implementation ViewController
@synthesize timerLabel;
- (void)viewDidLoad {
[super viewDidLoad];
// initialize the timer
_timer = [CPPTTimer createWithListener:self];
// start a 5 second timer
[_timer startTimer:5];
}
# pragma mark CPPTTimerListener methods
- (void)timerEnded {
NSLog(@"Obj-C: timerEnded.");
}
- (void)timerTicked:(int32_t)secondsRemaining {
NSLog(@"Obj-C: timerTicked with %d seconds remaining.", secondsRemaining);
// without dispatch_async, background thread warning is thrown
dispatch_async(dispatch_get_main_queue(), ^{
timerLabel.text = [NSString stringWithFormat:@"%d", secondsRemaining];
});
}
@end
【问题讨论】:
-
dispatch_async是不是objective-c;如果您包含正确的 GCD 标头,则不应有任何理由无法从 C++ 代码中调用它。另外,作为后台线程与 C++ 或任何其他语言有关。您正在做的事情是在 main 以外的线程上运行有问题的代码。 -
@BradAllred 是有道理的。所以看起来罪魁祸首很可能是
boost::thread,它启动了一个不同于主线程的新线程。 -
主要问题已经得到解答,但请注意,虽然 dispatch_async 不是 ObjC,但它是特定于 Apple 平台的。如果您的目标是让您的 C++ 代码与平台无关,您可以使用另一个 Djinni 接口来公开 callOnUIThread() 方法,您可以在 ObjC 中使用 dispatch_async for iOS 来实现该方法,然后在 Java 中使用
new Handler(Looper.getMainLooper()).post()。 Djinni 有一个示例 platform_threads 接口,它不这样做,但会创建线程,这与每个平台代码的需求非常相似,必须在特定线程上回调 C++ 代码。 -
很高兴知道,谢谢@atwyman!
标签: c++ objective-c xcode boost-asio djinni