Жизненный цикл объекта
Создание объекта
Других способов создания объектов (экземпляров класса), кроме как с использованием предикатов, объявленных для данного класса конструктором (явно или не явно), нет. Если конструктор не объявлен, то может быть использован предопределенный конструктор new().
Гибель объекта
VIP не содержит никаких средств для уничтожения объектов. Тем не менее они не живут вечно (до конца жизни приложения). Время жизни объектов определяется сборщиком мусора (GarbageCollector), встроенного в RunTime часть VIP. Сборщик мусора считает, что память, занимаемая объектом (экземпляром), может быть освобождена, когда исчезнут все ссылки на объект. И это почти единственное правило, определяющее время жизни объекта (экземплляра). Исходя из этого правила объект, созданный в пределах одного клоза, если ссылка на него не сохранена, погибает по завершении клоза. Например,
clauses мойПример():- МойОбъект=мойОбъект::new(), МойОбъект:мойПредикатИзМойОбъект(), ... последнийПредикатКлоза().
Здесь указатель на экземпляр класса мойОбъект сохраняется в переменной МойОбъект. Поэтому, пока клоз мойПример выполняется, живет и доступен через указатель МойОбъект и экземпляр мойОбъект. По правилам Пролога, переменные в клозах являются локальными и сохраняют свои значения только в пределах клоза. Следовательно, по завершении выполнения клоза мойПример объектмойОбъект считается недоступным и может умереть. Если, конечно, он не породил ссылок на себя самого в какой-нибудь постоянной памяти во время вызова конструктора или других своих предикатов. Случай
clauses мойПример():- МойОбъект=мойОбъект::new(), МойОбъект:мойПредикатИзМойОбъект(...), ... вызовПредикатаДругогоКлоза(МойОбъект), ... последнийПредикатКлоза(...).
не меняет сути - пока выполняется предикат вызовПредикатаДругогоКлоза(МойОбъект) экземпляр продолжает жить и умрет после завершения выполнения предиката последнийПредикатКлоза(...).
В большинстве случаев для построения более или менее полезной и хорошо организованнй программы этого недостаточно. Объект, содержащий данные, используемые другими объектами, должен жить, пока это необходимо и должет быть доступен.
Для этой цели используется хранение укзателей на объекты в фактах. В VIP как предикаты, так и факты типизированы. А это значит, что при декларировании факта (а равно и предиката) необходимо указание домена - либо предопределенного в языке, либо объявленного в программе. Применительно к указателям на объекты, специального объявления домена, связанного с объектами, не существует. Здесь действует правило: объявление интерфейса класса является одновременно и объявлением домена, определяющего этот класс.
Поэтому имя интерфейса используется в качестве домена при декларировании фактов для хранения указателей или их передачи в качестве параметров. Например,
class мойКласс:мойИнтерфейс end classмойКласс
уже определяет то обстоятельство, что все экземпляры класса мойКласс принадлежат домену мойИнтерфейс. Поэтому объявление факта либо в виде
facts мойУказатель_F:(string УдобноеИмяЭкземпляра,мойИнтерфейс УказательНаЭкземпляр).
либо в виде
facts мойУказатель_V:мойИнтерфейс:=erroneous.
Являются правильными (не путать с целесообразностью) объявлениями. Тогда в примере
clauses мойПример():- МойОбъект=мойОбъект::new(), МойОбъект:мойПредикатИзМойОбъект(...), мойУказатель_V:=МойОбъект, ... вызовПредикатаДругогоКлоза(МойОбъект), ... последнийПредикатКлоза(...).
время жизни объекта продлевается за пределы времени жизни клоза. Объект будет существовать все то время, пока на него существует указатель. Когда объект больше не нужен, достаточно удалить ссылку на него, например так:
... мойУказатель_V:=erroneous, ...
или так
... retract(мойУказатель_F("ПочтовыеАдреса",_УказательНаЭкземпляр)), ...
При этом следует иметь в виду, что смерть объекта ведет к смерти всех объектов, на которые данный объект хранит ссылки.
Тогда, например, если имеется дерево, построенное на основе ссылок на объекты, то потеря указателя на корень дерева ведет к смерти всех объектов, входящих в дерево.
Следует, однако, иметь в виду некоторые исключения из этого правила: Как показал опыт, если объект хранит указатель на окно (домен vpiDomains::windowHandle), то сборщик мусора будет рассматривать этот объект, как используемый.
То есть физически окно может быть закрыто, но неудаление указателя на это окно оставит объект в памяти. Аналогично часто указатели на COM-объекты могут быть причиной неособождения памяти сборщиком мусора.
Существует один замечательный предопределенный интерфейс, который, как и все интерфейсы является и доменом. Это интерфейс - object.
Все экземпляры, принадлежащие доменам своих интерфейсов, в то же время являются дочерними доменами домена object. Это значит, что практически возможно вместо
facts мойУказатель_V:мойИнтерфейс:=erroneous.
объявлять
facts мойУказатель_V:object:=erroneous.
При этом будет компилироваться и работать
clauses мойПример():- мойУказатель_V:=мойОбъект::new(), ...
Но вот вызов
... мойУказатель_V:мойПредикатИзМойОбъект(...), ...
в этом случае не пропустит компилятор. Вместо этого необходимо прoизводить конвертацию
... МойУказатель=convert(мойИнтерфейс,мойУказатель_V), МойУказатель:мойПредикатИзМойОбъект(...), ...
Примерно то же происходит, если интерфейс поддерживает другие интерфейсы. Сборщик мусора можно подтолкнуть предикатом
memory::garbageCollect()
но обычно в этом нет никакой необходимости.
Следует иметь в виду, что момент, когда память будет фактически освобождена, и будет вызван предикат finalize, не контролируется и определяется внутренней логикой сборщика мусора и его взаимодействием с операционной системой.