Жизненный цикл объекта: различия между версиями
(не показано 12 промежуточных версий 2 участников) | |||
Строка 1: | Строка 1: | ||
==Создание объекта== | ===Создание объекта=== | ||
Других способов создания объектов (экземпляров), кроме как с использованием предикатов, объявленных для данного класса конструктором, нет. Если конструктор не объявлен, то может быть использован предопределенный конструктор new(). | Других способов создания объектов (экземпляров класса), кроме как с использованием предикатов, объявленных для данного класса конструктором (явно или не явно), нет. Если конструктор не объявлен, то может быть использован предопределенный конструктор new(). | ||
==Гибель объекта== | |||
VIP не содержит никаких средств для уничтожения | ===Гибель объекта=== | ||
VIP не содержит никаких средств для уничтожения объектов. Тем не менее они не живут вечно (до конца жизни приложения). Время жизни объектов определяется сборщиком мусора (''GarbageCollector''), встроенного в ''RunTime'' часть VIP. Сборщик мусора считает, что память, занимаемая объектом (экземпляром), может быть освобождена, когда исчезнут все ссылки на объект. И это почти единственное правило, определяющее время жизни объекта (экземпляра). | |||
Исходя из этого правила объект, созданный в пределах одного клоза, если ссылка на него не сохранена, погибает по завершении клоза. | Исходя из этого правила объект, созданный в пределах одного клоза, если ссылка на него не сохранена, погибает по завершении клоза. | ||
Например, | Например, | ||
< | <vip>clauses | ||
мойПример():- | мойПример():- | ||
МойОбъект=мойОбъект::new(), | МойОбъект=мойОбъект::new(), | ||
МойОбъект:мойПредикатИзМойОбъект( | МойОбъект:мойПредикатИзМойОбъект(), | ||
... | ... | ||
последнийПредикатКлоза( | последнийПредикатКлоза(). | ||
</ | </vip> | ||
Здесь указатель на экземпляр класса ''мойОбъект'' сохраняется в переменной ''МойОбъект''. Поэтому, пока клоз ''мойПример'' выполняется, живет и доступен через указатель ''МойОбъект'' и экземпляр ''мойОбъект''. | Здесь указатель на экземпляр класса ''мойОбъект'' сохраняется в переменной ''МойОбъект''. Поэтому, пока клоз ''мойПример'' выполняется, живет и доступен через указатель ''МойОбъект'' и экземпляр ''мойОбъект''. | ||
По правилам Пролога, переменные в клозах являются локальными и сохраняют свои значения только | По правилам Пролога, переменные в клозах являются локальными и сохраняют свои значения только в пределах клоза. Следовательно, по завершении выполнения клоза ''мойПример'' объект''мойОбъект'' считается недоступным и может умереть. Если, конечно, он не породил ссылок на себя самого в какой-нибудь постоянной памяти во время вызова конструктора или других своих предикатов. | ||
Случай | Случай | ||
<vip>clauses | <vip>clauses | ||
Строка 25: | Строка 26: | ||
не меняет сути - пока выполняется предикат ''вызовПредикатаДругогоКлоза(МойОбъект)'' экземпляр продолжает жить и умрет после завершения выполнения предиката ''последнийПредикатКлоза(...)''. | не меняет сути - пока выполняется предикат ''вызовПредикатаДругогоКлоза(МойОбъект)'' экземпляр продолжает жить и умрет после завершения выполнения предиката ''последнийПредикатКлоза(...)''. | ||
В большинстве случаев для построения более или менее полезной и хорошо | В большинстве случаев для построения более или менее полезной и хорошо организованной программы этого недостаточно. Объект, содержащий данные, используемые другими объектами, должен жить, пока это необходимо и должен быть доступен. | ||
Для этой цели используется хранение | Для этой цели используется хранение указателей на объекты в фактах. В VIP как предикаты, так и факты типизированы. А это значит, что при декларировании факта (а равно и предиката) необходимо указание домена - либо предопределённого в языке, либо объявленного в программе. | ||
Применительно к указателям на объекты, специального объявления домена, связанного с объектами, не существует. Здесь действует правило: объявление интерфейса класса является одновременно и объявлением домена, определяющего этот класс. | Применительно к указателям на объекты, специального объявления домена, связанного с объектами, не существует. Здесь действует правило: объявление интерфейса класса является одновременно и объявлением домена, определяющего этот класс. | ||
Строка 33: | Строка 34: | ||
Например, | Например, | ||
<vip>class мойКласс:мойИнтерфейс | <vip>class мойКласс:мойИнтерфейс | ||
end | end class мойКласс</vip> | ||
уже определяет то обстоятельство, что все экземпляры класса мойКласс принадлежат домену ''мойИнтерфейс''. | уже определяет то обстоятельство, что все экземпляры класса мойКласс принадлежат домену ''мойИнтерфейс''. | ||
Поэтому объявление факта либо в виде | Поэтому объявление факта либо в виде | ||
Строка 40: | Строка 41: | ||
либо в виде | либо в виде | ||
<vip>facts | <vip>facts | ||
мойУказатель_V:мойИнтерфейс:=erroneous.<vip> | мойУказатель_V:мойИнтерфейс:=erroneous.</vip> | ||
Являются правильными (не путать с целесообразностью) объявлениями. | Являются правильными (не путать с целесообразностью) объявлениями. | ||
Тогда в примере | Тогда в примере | ||
Строка 68: | Строка 69: | ||
Как показал опыт, если объект хранит указатель на окно (домен ''vpiDomains::windowHandle''), то сборщик мусора будет рассматривать этот объект, как используемый. | Как показал опыт, если объект хранит указатель на окно (домен ''vpiDomains::windowHandle''), то сборщик мусора будет рассматривать этот объект, как используемый. | ||
То есть физически окно может быть закрыто, но | То есть физически окно может быть закрыто, но не удаление указателя на это окно оставит объект в памяти. Аналогично часто указатели на COM-объекты могут быть причиной не освобождения памяти сборщиком мусора. | ||
Существует один замечательный | Существует один замечательный предопределённый интерфейс, который, как и все интерфейсы является и доменом. Это интерфейс - object. | ||
Все экземпляры, | Все экземпляры, принадлежащие доменам своих интерфейсов, в то же время являются дочерними доменами домена object. | ||
Это значит, что практически возможно вместо | Это значит, что практически возможно вместо | ||
<vip>facts | <vip>facts | ||
Строка 89: | Строка 90: | ||
...</vip> | ...</vip> | ||
в этом случае не пропустит компилятор. | в этом случае не пропустит компилятор. | ||
Вместо этого необходимо | Вместо этого необходимо производить конвертацию | ||
<vip> ... | <vip> ... | ||
МойУказатель=convert(мойИнтерфейс,мойУказатель_V), | МойУказатель=convert(мойИнтерфейс,мойУказатель_V), | ||
Строка 95: | Строка 96: | ||
...</vip> | ...</vip> | ||
Примерно то же происходит, если интерфейс поддерживает другие интерфейсы. | Примерно то же происходит, если интерфейс поддерживает другие интерфейсы. | ||
Сборщик мусора можно подтолкнуть предикатом | Сборщик мусора можно подтолкнуть предикатом | ||
<vip>memory::garbageCollect()</vip> | <vip>memory::garbageCollect()</vip> но обычно в этом нет никакой необходимости. | ||
===Финализатор=== | |||
Существует специальный предикат, который вызывается в момент гибели объекта, перед освобождением памяти объекта. Этот предикат - '''finalize'''. Его нельзя объявить, но для объектных классов можно написать реализацию (клауз). | |||
Следует иметь в виду, что момент, когда память будет фактически освобождена, и будет вызван предикат '''finalize''' (если он есть), не контролируется и определяется внутренней логикой сборщика мусора и его взаимодействием с операционной системой. Также нельзя делать никаких предположений о порядке освобождения объектов и, соответственно, о порядке вызова '''finalize''' для разных объектов. Рекомендуется предикат '''finalize''' использовать исключительно для освобождения ресурсов, при этом объектные факты освобождать нет никакой необходимости. Раз уж объект удаляется, то и все его факты постигнет аналогичная участь. | |||
Так же не рекомендуется закладывать в финализатор выполнение каких-нибудь логических действий. Например, если закрытие файла делать в финализаторе, то файл будет отрыт, а доступ других программ будет заблокировано, до неизвестного момента, пока сборщик мусора соизволит удалить данный объект. | |||
Следует учитывать и то, что финализатор может быть вызван на не полностью инициализированном объекте. Например, если произошёл сбой при выполнении конструктора, то не все факты будут правильно инициализированы. Поэтому всегда следует проверять факт, например на '''erroneous'''. | |||
Ещё одно замечание, нельзя заранее сказать на каком потоке (thread) будет выполняться сборщик мусора (неявный вызов) и, соответственно, выполняться финализаторы. Распространение исключения из финализатора может повредить логику работы сборщика. В связи с эти все исключения из финализаторов перехватываются [[Обработка исключений|try/catch]] и игнорируется. Т.е. как будто вызов финализатора происходит так: | |||
<Vip>try | |||
Object:finalize() | |||
catch _ do | |||
succeed() | |||
end try</Vip> | |||
[[Категория:VipLanguage]] |
Текущая версия на 11:17, 20 декабря 2007
Создание объекта
Других способов создания объектов (экземпляров класса), кроме как с использованием предикатов, объявленных для данного класса конструктором (явно или не явно), нет. Если конструктор не объявлен, то может быть использован предопределенный конструктор 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:мойПредикатИзМойОбъект(...), ...
в этом случае не пропустит компилятор. Вместо этого необходимо производить конвертацию
... МойУказатель=convert(мойИнтерфейс,мойУказатель_V), МойУказатель:мойПредикатИзМойОбъект(...), ...
Примерно то же происходит, если интерфейс поддерживает другие интерфейсы. Сборщик мусора можно подтолкнуть предикатом
memory::garbageCollect()
но обычно в этом нет никакой необходимости.
Финализатор
Существует специальный предикат, который вызывается в момент гибели объекта, перед освобождением памяти объекта. Этот предикат - finalize. Его нельзя объявить, но для объектных классов можно написать реализацию (клауз). Следует иметь в виду, что момент, когда память будет фактически освобождена, и будет вызван предикат finalize (если он есть), не контролируется и определяется внутренней логикой сборщика мусора и его взаимодействием с операционной системой. Также нельзя делать никаких предположений о порядке освобождения объектов и, соответственно, о порядке вызова finalize для разных объектов. Рекомендуется предикат finalize использовать исключительно для освобождения ресурсов, при этом объектные факты освобождать нет никакой необходимости. Раз уж объект удаляется, то и все его факты постигнет аналогичная участь. Так же не рекомендуется закладывать в финализатор выполнение каких-нибудь логических действий. Например, если закрытие файла делать в финализаторе, то файл будет отрыт, а доступ других программ будет заблокировано, до неизвестного момента, пока сборщик мусора соизволит удалить данный объект. Следует учитывать и то, что финализатор может быть вызван на не полностью инициализированном объекте. Например, если произошёл сбой при выполнении конструктора, то не все факты будут правильно инициализированы. Поэтому всегда следует проверять факт, например на erroneous.
Ещё одно замечание, нельзя заранее сказать на каком потоке (thread) будет выполняться сборщик мусора (неявный вызов) и, соответственно, выполняться финализаторы. Распространение исключения из финализатора может повредить логику работы сборщика. В связи с эти все исключения из финализаторов перехватываются try/catch и игнорируется. Т.е. как будто вызов финализатора происходит так:
try Object:finalize() catch _ do succeed() end try