通过C++从后台线程错误修改自动布局引擎
Modifying the autolayout engine from a background thread error, from C++
当我通过双向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中的问题:
是否收到"此应用程序正在修改自动布局引擎"错误?
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
对UI类的所有访问都必须在主线程上进行。您的提升计时器没有在主线程上运行。
因此,让计时器在主线程上启动可能是有意义的。您可以使用标准libdispatch API,即使使用纯C++代码(不必是.mm ObjC++)。
请确保将#include <dispatch/dispatch.h>
添加到CPP实现文件中。
以下代码更改将确保计时器始终在Cocoa主线程上运行。
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();
});
}
}
}
我认为该代码的其余部分是有效的。我所做的只是修改回调的调用方式。请注意,我们创建了listener_
shared_ptr
和time_remaining_
值的副本。这些将被在主线程上执行的块捕获(并复制)。
如果您可以保证this
在该块执行之前不会删除,那么您可以隐式捕获this
。。。
dispatch_async(dispatch_get_main_queue(), ^{
listener_->TimerTicked(time_remaining_);
});
或者,如果您从中启用共享,您可以创建指向this
的共享指针的副本,并以这种方式捕获它。。。
auto self = shared_from_this();
dispatch_async(dispatch_get_main_queue(), ^{
self->listener_->TimerTicked(self->time_remaining_);
});
有很多方法可以做到这一点,但这可能是最简单的,现在您可以确保所有计时器都能启动到Cocoa主线程。
- 使用C++库在Android项目中修改gradle中的cmake参数,用于插入指令的测试
- 'short int'持有的值溢出,但"自动"不会溢出?
- 如何使用C/C++在MacOSX中获得键盘布局
- 独立读取-修改-写入顺序
- 当系统的卷被修改时,如何修改WASAPI环回捕获卷
- 修改函数中的指针(将另一个指针作为参数传递)
- 普通环路未使用gcc 4.8.5自动矢量化
- 修改Mongodb源代码以禁用文档的自动生成"_id"字段
- 在 lambda 表达式中自动推导出的类型是什么,用于修改 bool 类型的向量(特殊容器)
- 在击中断点并继续执行VS时,如何在运行时自动修改变量的值
- 自动或自动&&是否首选修改具有基于范围的foor循环的范围?
- 我如何确保修改.H文件时,使用Visual Studio 2008将包括其在内的.cc文件自动编译
- 如何使Qt网格布局自动调整列宽的大小
- 布局问题:当表单的布局设置为垂直时,组框中的Qlistbox会自动展开
- 通过C++从后台线程错误修改自动布局引擎
- 自动输入到我无法修改的二进制文件
- Qt在布局中自动安排小部件
- wxWidgets -大小禁用自动布局
- c++.结构在不同平台上的填充/对齐和布局兼容性的自动检查
- 为什么C++项目在修改它所依赖的 C# 项目时不会自动重新生成?