【发布时间】:2018-04-07 17:49:50
【问题描述】:
我有一个 Layer 类,其中包含某些对象,如按钮、滑块等。我已经构建了 Layer 类,以便它能够自我刷新。本质上,它会删除所有/大部分对象,然后重新创建它们。原因是我有各种功能,比如更改字体大小或 UI 的主题,我希望这些更改立即显而易见。
始终如一地完成此任务的唯一方法是简单地重新构建所有内容,因为主题和字体大小会影响所有内容的外观和布局。这一切都运作良好,我对系统感到满意。
但是,当属于该层的对象启动刷新时,就会出现问题(我所说的问题是指崩溃)。例如,我有一个字体大小滑块,它应该更改字体大小,然后刷新图层。它有一个对层刷新函数的回调,由 lambda 捕获,并存储在成员 std::function 对象中。当函数执行时会发生什么,在调用函数完成之前的某个时刻,滑块当然会被破坏并与它一起破坏函数。因此,当前正在运行的函数停止存在并且程序崩溃。
我的问题是,在这种矛盾的情况下,实现这种行为不会导致崩溃的良好而优雅的方法是什么?
编辑:代码
class MySlider : public MyNode
{
//... various other class members
std::function<void()> functionOnRelease = nullptr;
MySlider::MySlider(/*constructor arguments*/)
{
this->preferenceKey = key;
slider->addTouchEventListener([=](Ref* sender, ui::Widget::TouchEventType type)
{
if (type == ui::Widget::TouchEventType::ENDED)
on_release();
});
//... remainder of constructor body
}
void MySlider::set_function_on_release(const std::function<void()>& functionOnRelease)
{
this->functionOnRelease = functionOnRelease;
}
void on_release()
{
if (preferenceKey.empty() == false)
PREFM->set_preference(preferenceKey, slider->getPercent() + min);
if (functionOnRelease)
functionOnRelease();
//the program crashes at this point when the slider gets destroyed
}
}
//The layer in question and the function that creates its slider
void Options::create_font_size_slider()
{
NodeVector sliders;
//there's a static create function convention imposed by the engine
auto fontSizeSlider = MySlider::createWithPreferenceAutomatic("Font Size: ", "FontSizePercentage", 50, 150, false, sliders);
fontSizeSlider->set_function_on_release([=] { this->refresh(); /*this kills the slider*/});
}
编辑2:
我已经设法通过一个简单而丑陋的 hack 来避免崩溃。在稍有延迟(0.05 秒)后,我已强制 refresh() 函数在单独的线程上发生。这为所有事情的发生提供了足够的时间,而不会出现明显的延迟。如果设备真的很慢和奇怪的线程调度,我可以忍受潜在的偶尔崩溃,因为需要完成的所有事情都在崩溃发生之前完成。
【问题讨论】:
-
重新创建每个对象只是为了刷新似乎有点过分。你不能在每个对象上使用类似
update()的函数吗? -
如果没有设想更改会影响整个 UI,我可以。例如,如果我更改主题,新主题将具有不同的字体,并且可以具有不同的精灵大小。这意味着所有对象的大小也必须更新,这也意味着它们的相对位置需要更新。此外,有许多不同的类以不同的方式运行,因此我必须单独定制所有更新功能。理论上,我可以构建这样一个系统,但它会非常复杂且容易出错。重建一切可以完美地完成工作。
-
我不明白你的意思是滑块破坏了它的功能。我认为您需要向我们展示相关代码。在我看来,刷新/重新创建整个层的功能以某种方式归层中包含的控件类之一所有,这似乎不太合乎逻辑。
-
我基本上只会在除名称之外的所有内容中重新创建当前行为,唯一的好处是对象永远不会停止存在。它可以解决函数的这个特殊问题,但执行的工作量几乎相同,而且我需要维护大量新代码。
-
函数本身不属于对象。该对象以 std::function 的形式拥有一个指向它的指针。具体来说,函数调用被包装在一个 lambda 中,该 lambda 也捕获了层的指针。这个 lambda 被存储在 std::function 中。当对象被销毁时,被调用并导致崩溃的 std::function 也会被销毁。我现在将在问题中发布一些代码。