从后台线程错误修改自动布局引擎,从C ++ [英] Modifying the autolayout engine from a background thread error, from C++

查看:183
本文介绍了从后台线程错误修改自动布局引擎,从C ++的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在Xcode 7.1中,通过双向djinni架构从C ++进行UI调用时,会出现以下错误:



正在从后台线程修改自动布局引擎,这可能导致引擎损坏和奇怪的崩溃。这将导致在以后的版本中的异常。



我能够解决Objective-C的问题, / p>

获取此应用正在修改自动布局引擎错误?

  dispatch_async(dispatch_get_main_queue(),^ {
// code here
});

我的问题是,是否有办法以某种方式从C ++中完成这一点,而不必调用dispatch_async Objective-C每次调用UI?






发布与自动生成的相关代码源文件省略,完整项目也可在 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
$ b

  #pragma once 

#include< boost / asio.hpp>

#includetimer.hpp
#includetimer_listener.hpp

命名空间cpptimer {

类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 p>

  #include< boost / bind.hpp> 
#include< boost / thread.hpp>

#includetimer_impl.hpp

命名空间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.\\\
;
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> 

#importCPPTTimerListener.h

@interface ViewController:UIViewController< CPPTTimerListener>

@property(nonatomic,strong)IBOutlet UILabel * timerLabel;

@end

ViewController.m / p>

  #importViewController.h
#importCPPTTimer.h

@ interface ViewController(){
CPPTTimer * _timer;
}

@end

@implementation ViewController

@synthesize timerLabel;

- (void)viewDidLoad {

[super viewDidLoad];

//初始化定时器
_timer = [CPPTTimer createWithListener:self];

//开始一个5秒定时器
[_timer startTimer:5];

}

#pragma mark CPPTTimerListener方法

- (void)timerEnded {
NSLog(@Obj-C:timerEnded。 );
}

- (void)timerTicked:(int32_t)secondsRemaining {
NSLog(@Obj-C:timerTicked with%d seconds remaining。,secondsRemaining);
//没有dispatch_async,抛出后台线程警告
dispatch_async(dispatch_get_main_queue(),^ {
timerLabel.text = [NSString stringWithFormat:@%d,secondsRemaining];
});
}

@end


解决方案>

所有访问UI类必须必须在主线程上进行。您的提升计时器不会在主线程上运行。



因此,在主线程上启用定时器可能是有意义的。您可以使用标准的libdispatch API,甚至使用纯C ++代码(不必是.mm ObjC ++)。



一定要添加



下面的代码更改将确保计时器始终运行在您的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.\\\
;

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 将不会在该块执行之前删除,那么您可以隐式捕获 ...

  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主线程。


I get the following error in Xcode 7.1 when making a UI call from C++, through a two-way djinni architecture:

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.

I am able to solve the problem in Objective-C with the solution given here:

Getting a "This application is modifying the autolayout engine" error?

dispatch_async(dispatch_get_main_queue(), ^{
    // code here
});

My question is, is there a way to somehow accomplish this from within C++ without having to call dispatch_async in Objective-C at every call to the UI? Or is every call from C++ considered a background thread as far as Xcode is concerned?


Posting relevant code with auto-generated source files omitted, full project also available on 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

解决方案

All access to the UI classes must take place on the main thread. Your boost timer does not run on the main thread.

Thus, it probably makes sense to have your timer fire on the main thread. You can use the standard libdispatch API, even with pure C++ code (does not have to be .mm ObjC++).

Be sure to add #include <dispatch/dispatch.h> to your CPP implementation file.

The following code change will make sure that the timer always runs on the Cocoa main 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 that code works. All I did was modify how the callbacks are invoked. Note that we create a copy of the listener_ shared_ptr and the time_remaining_ value. These will be captured (and copied) by the block that gets executed on the main thread.

If you can guarantee that this will not delete before that block executes, then 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 capture it that way...

auto self = shared_from_this();
dispatch_async(dispatch_get_main_queue(), ^{
   self->listener_->TimerTicked(self->time_remaining_);
});

There are a number of ways to do this, but this may be the easiest, and now you ensure all your timers fire through to the Cocoa main thread.

这篇关于从后台线程错误修改自动布局引擎,从C ++的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆