Я действительно оценил преимущества использования объектов для развертывания данного приложения в среде DigitalMicrograph с помощью языка DMS. Объектно-ориентированный подход открывает двери для использования многократно используемых шаблонов проектирования, включающих взаимодействующие объекты, например. Модель-представление-контроллер (MVC). Однако объекты в DM кажутся очень изменчивыми из-за использования автоматического подсчета ссылок для управления их жизненными циклами. Чтобы трио MVC или любой другой набор взаимодействующих объектов оставался в живых достаточно долго, чтобы быть полезным, по крайней мере один из них должен быть внедрен в энергонезависимый объект, управляемый приложением DM. До сих пор единственные такие объекты, с которыми я сталкивался в DM, основаны на классе UIFrame (т. е. немодальные диалоги и палитры пользовательского интерфейса). Для реализаций MVC это прекрасно работает, поскольку имеет смысл реализовать представление как объект UIFrame. Это немного необычно, поскольку объект View становится корневым объектом, который поддерживает жизнь и функционирование трио MVC. Обычно это объект Controller, который является корневым в приложении и управляет объектами Model и View. Но как насчет шаблонов проектирования, не связанных с пользовательским интерфейсом? Есть ли какой-либо (приемлемый) способ обеспечить постоянство набора взаимодействующих объектов без их укоренения в объекте UIFrame? Существуют ли другие типы объектов с корнем приложения, которые могут служить этой цели? Я предполагаю, что установка эталонного цикла не будет приемлемым подходом из-за неизбежного риска утечек памяти.
Как настроить постоянные взаимодействующие объекты в DigitalMicrograph с помощью сценариев?
Ответы (3)
Третье и, безусловно, лучшее и чистое решение — запустить ваш объект в качестве «слушателя» какого-либо события. Поскольку вы ищете объект, который должен оставаться в области видимости, пока DigitalMicrograph открыт, возможно, лучше всего слушать само приложение. Прослушивая сообщение «about_to_close», вы также получаете идеальный дескриптор для правильного освобождения всех ресурсов перед завершением работы. Код следующий:
Из моих 3 ответов это тот, который я бы использовал. (Остальные должны просто иллюстрировать варианты.)
class MyPermanentObject
{
MyPermanentObject( object self ) { result("created MyPermanentObject :"+self.ScriptObjectGetID()+"\n");}
~MyPermanentObject( object self ) { result("killed MyPermanentObject :"+self.ScriptObjectGetID()+"\n");}
void DeInitialize( object self, number eventFlags, object appObj )
{
OKDialog( "The application is closing now. Deinitialize stuff properly!" );
}
}
{
object listener = Alloc( MyPermanentObject )
ApplicationAddEventListener( listener, "application_about_to_close:DeInitialize" )
}
Я могу придумать разные способы добиться этого постоянства, но первый, который пришел мне в голову, — запустить один объект в фоновом потоке, как в примере ниже. Фактический фоновый поток может время от времени проверять, должен ли объект все еще оставаться, и, делясь идентификатором объекта с внешним миром, другие объекты (которые не должны быть постоянными) могут получить доступ к «привязанному» объекту.
Предупреждение: если вы храните такие вещи в памяти, вы должны быть осторожны при закрытии DigitalMicrograph. Если объект зависает на некоторых элементах, которые DM хочет уничтожить, вы можете увидеть ошибки или сбои в конце.
// This is the object "anchored". It will remain in memory, because we launch it on a separate thread.
// On this thread, it loops until a variable is set to false (or until SHIFT is pressed)
Class IPersist : Thread
{
number keepme
IPersist( object self ) { result("created IPersist:"+self.ScriptObjectGetID()+"\n");}
~IPersist( object self ) { result("killed IPersist:"+self.ScriptObjectGetID()+"\n\n\n\n");}
void CallFromOutside( object self ) { Result( "\t IPersist can be used!\n" ); }
void StopFromOutside( object self ) { keepme = 0; }
void RunThread( object self )
{
keepme = 1
Result( "\t Called once at start.\n")
While( keepme && !ShiftDown() ) yield()
Result( "\t Finished.\n")
}
}
// Just and example class used to access the 'anchored' object
Class SomethingElse
{
number keepID
SomethingElse( object self ) { result("created SomethingElse:"+self.ScriptObjectGetID()+"\n");}
~SomethingElse( object self ) { result("killed SomethingElse:"+self.ScriptObjectGetID()+"\n");}
void SetKeepID( object self, number id ) { keepID = id; }
void CallOut( object self )
{
result( "SomethingElse object is accessing CallOut...\n" )
object p = GetScriptObjectFromID( keepID )
if ( p.ScriptObjectIsValid() )
{
p.CallFromOutside()
}
}
void CallStop( object self )
{
result( "SomethingElse object is accessing CallOut...\n" )
object p = GetScriptObjectFromID( keepID )
if ( p.ScriptObjectIsValid() )
{
p.StopFromOutside()
}
}
}
// Main script. Create object on separate thread. Then feed it's ID as "weak reference" into the second object.
{
object ob = Alloc(IPersist)
ob.StartThread()
object other = Alloc(SomethingElse)
other.SetKeepID( ob.ScriptObjectGetID() )
other.CallOut()
If ( TwoButtonDialog( "You can either stop IPerstis now, or by pressing SHIFT later.", "Stop now", "later" ) )
other.CallStop()
}
Альтернативным способом было бы, чтобы два объекта сохраняли ссылки друг на друга. Это тупиковая ситуация, которую обычно лучше избегать, но с целью закрепления она также работает. Ни один объект не может выйти из области видимости, пока вы не отпустите его намеренно. Опять же, вы несете ответственность за «выпуск» вещей, когда вам нужно правильное завершение работы системы.
Код для тупиковой ситуации довольно тонкий:
class SelfLock
{
object partner
SelfLock( object self ) { result("created SelfLock:"+self.ScriptObjectGetID()+"\n");}
~SelfLock( object self ) { result("killed SelfLock:"+self.ScriptObjectGetID()+"\n");}
void SetPartner(object self, object p) { partner = p; }
void ReleasePartner(object self) { partner = NULL; }
}
{
object p1 = Alloc(SelfLock)
object p2 = Alloc(SelfLock)
p1.SetPartner(p2)
p2.SetPartner(p1)
if ( TwoButtonDialog( "Release partner", "Yes", "No keep locked" ) )
p1.ReleasePartner()
}