![]() |
Модераторы: Poseidon, Snowy, bems, MetalFan |
![]() ![]() ![]() |
|
Snowy |
|
||||||||||
![]() Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Модератор Сообщений: 11363 Регистрация: 13.10.2004 Где: Питер Репутация: 192 Всего: 484 |
Как ни странно, но большинство дельфистов не имеют представления о том, что это за TPersistent такой и зачем он нужен.
Некоторые вообще даже не знают о его существовании. А ведь это один из базовых классов. Данная статья написана мной в результате поиска решения о сохранении настроек и параметров. Так я и пришел к TPersistent и использованию его возможностей. И так статья: TPersistent Виртуальный класс – наследник TObject. Является базовым классом – предком большинства стандартных классов. Основное свойство данного класса заключается в его названии – Хранимый. То есть класс, который может быть сохранен и впоследствии восстановлен. Сам TPersistent не имеет методов для сохранения и восстановления. Данную работу реализуют классы, работающие с потоками (stream). Наиболее очевидна его работа на примере TForm. В отличии от той же JAVA мы не прописываем параметры объектов, расположенных на форме. Вместо этого создается dfm файл, в котором и сохраняются все эти данные. При компиляции программы, он помещается в ресурсы. При создании формы, он читается из ресурсов, как наследник TPersistent. Применение Итак, как же можно использовать TPersistent? Естественно сам по себе он не представляет особой ценности. Ценностью являются его свойства. Мы можем легко сохранять и читать свои объекты, пронаследовав их не от TObject, а от TPersistent. Зачем это нужно? Ответ прост. Нам постоянно приходится сохранять какие-то настройки, параметры. Придумано множество вещей для данных целей: ini файлы, реестр, xml и другие умные вещи. TPersistent позволяет забыть про это, как про страшный сон. Не нужно разбирать параметры, сравнивать версии, конвертировать типы данных. Всего лишь нужно сказать: сохрани или прочитай этот объект. А как же версии? Если в следующей версии Вашей программы, в Вашем объекте появятся новые поля, то прочитать файл не составит никакого труда – новые поля просто не будут прочитаны, т.к. Их нет в файле. Но все остальные поля будут корректно прочитаны, а впоследствии объект будет сохранен уже с новым полем. Сложнее, если вы убираете одно из полей. Но и тут ничего страшного – помогает блок try .. except, который в готовой программе и заметен не будет. Все остальные поля также будут корректно прочитаны, а впоследствии корректно сохранены. Как? Теперь непосредственно перейдем к вопросу, как это делать. Для сохранения и чтения существуют 2 класса: TReader и TWriter. Они умеют сохранять разные типы данных, в том числе и объекты-наследники TPersistent. Есть правда одно НО. В Borland почему-то решили, что сохранять персистенты программистам совсем не нужно. Хотя это мало кого может остановить. Чтобы активировать эту возможность, нужно всего лишь пронаследовать TWriter и TReader:
Это все. Никакого кода – просто наследуем. Это открывает нам protected методы для сохранения объектов-наследников TPersistent. Далее приведу код модуля, который будет сохранять и читать объекты.
Что же в этом коде. 4 процедуры: procedure WriteObj(obj: TPersistent; st: Tstream); Просто создает TWriter, сохраняет объект, уничтожает TWriter. procedure ReadObj(obj: TPersistent; st: TStream); Просто создает TReader, читает все параметры объекта, уничтожает TReader. procedure SaveObjToFile(obj: TPersistent; FileName: string); Это пример реализации сохранения объекта, на примере TFileStream. procedure LoadObjFromFile(obj: TPersistent; FileName: string); Это пример реализации чтения объекта, на примере TFileStream. Принцип работы простой – создали стрим, сохранили/прочитали объект, уничтожили стрим. Пример №1 – один объект Теперь перейдем к конкретному примеру. У нас есть объект, в котором мы храним какие-то настройки или просто какие-то данные нашей программы. Сохраним и прочитаем его, используя unit Saver.
Для примера я взял простой объект с двумя полями – дата и строка. Здесь все просто – создали объект, прочитали из файла. Сохранили, уничтожили. Можно было сохранение и загрузку поместить в деструктор и конструктор соответственно. Но я не стал этого делать, чтобы не раздувать код. В папке '1' (в прилагаемом файле) можно найти пример работы с этим кодом. Как это работает? Как вы наверное заметили, в нашем объекте есть private данные и published property. Зачем было делать именно так, а не просто создать данные в разделе public? Смысл в том, что сохраняется только RTTI информация. То есть та, которая находится в разделе published. Данные, расположенные в разделе publishet имеют информацию об именах полей и их типах, в то время, как остальная информация это всего лишь поля в памяти. А для сохранения эта информация нам нужна. При этом данные могут вообще не существовать. Важны лишь property. Они могут вообще не ссылаться ни на какие данные, а только иметь функции для получения и установки значения. Пример №2 – несколько объектов Пока вроде все просто. Берем объект, сохраняем в стрим, читаем. Но возникает ряд вопросов. Например, как сохранить группу объектов в один файл, как сохранять только необходимые данные, а не все, для чего можно использовать property, не связанные с данными объекта. Я решил объеденить все это в один пример. Для примера я взял всего 2 объекта, хотя их может быть сотни. Первый объект нам уже знаком, я не буду его описывать – беру из прошлого примера без изменения. Второй объект интереснее. Данный объект позволяет нам сохранять 4 основных параметра главной формы – положение и размеры. Сам объект при этом не хранит никаких данных. Самый простой способ сохранения всех объектов вместе – это сделать сами объектры полями одного общего объекта, который мы и будем сохранять.
Теперь приведу пример, как это использовать:
Данный код содержится в папке '2' архива. Можете посмотреть его в действии. На мой взгляд, пример довольно полезный. Таким образом мы можем для каждой формы нашей программы создать наследник класса, сохраняющего параметры формы, добавив всего пару строчек кода. Возможные проблемы Пока что на единственный момент, на который я натолкнулся – property должны иметь read и write параметры, чтобы они сохранялись. Если не будет одного из них – поле сохраняться не будет. Даже объект, указатель на который вы не собираетесь назначать, должен иметь параметр write. Если у Вас найдутся другие проблемы, спрашивайте. Assign Поскольку данная статья является описанием класса TPersistent, я не могу не упомянуть еще одно отличие от TObject. Данный класс реализует открытый метод Assign, который производит присвоение полей нашему объекту от другого. Но, думаю, назначение метода Assign известно каждому. Поэтому не буду на нем останавливаться. Замечу лишь, что в самом TPersistent этот метод не производит никаких действий. Он только вызывает protected метод AssignTo, который и должны наследовать его потомки. Заключение Итак, TPersistent является удобным средством для сохранения и восстановления объектов без применения сторонних модулей. Данный механизм уже имеется в Вашей программе, если Вы используете модули Classes или Forms. Поэтому это не добавит размера Вашей программе. Для сохранения мы не придумываем ничего нового, а просто используем стандартный механизм. Остались нераскрыты некоторые моменты. Например, как сохранять объекты в один файл, не объединяя их в один класс. Но это уже не входит в стандартный механизм и требует отдельной реализации. Также не рассмотрены варианты работы с другими типами стримов. А в качестве таковых можно рассматривать любые. Отдельного внимания может заслуживать TResourceStream. Мы можем сохранить наши объекты в файл,а потом прилинковать к нашему exe в качестве ресурса. Если вы захотите расширить данную статью своими исследованиями на эту тему – пишите, обсудим. Жду Ваших замечаний и дополнений. Сув. Snowy. Присоединённый файл ( Кол-во скачиваний: 120 ) ![]() |
||||||||||
|
|||||||||||
Alexeis |
|
||||
![]() Амеба ![]() Профиль Группа: Админ Сообщений: 11743 Регистрация: 12.10.2005 Где: Зеленоград Репутация: 109 Всего: 459 |
Snowy, в модуле Saver исползуется процедура
Что значит число 64, которое передается в качестве параметра конструктору. ------------------------------------------------------------------------------------ Странно но при компиляции первого примера у меня выскачило Undeclarated identifier "Self" на строку
Что он должен понимать в данном случае как Self Это сообщение отредактировал(а) alexeis1 - 2.5.2006, 13:41 -------------------- Vit вечная память. Обсуждение действий администрации форума производятся только в этом форуме гениальность идеи состоит в том, что ее невозможно придумать |
||||
|
|||||
Rouse_ |
|
|||
![]() Опытный ![]() ![]() Профиль Группа: Участник Сообщений: 469 Регистрация: 23.4.2005 Репутация: 5 Всего: 29 |
У TComponent-а в protected есть методы WriteState и ReadState...
Но это так, к слову ;) |
|||
|
||||
~FoX~ |
|
|||
![]() НЕ рыжий!!! ![]() ![]() ![]() ![]() Профиль Группа: Участник Клуба Сообщений: 2819 Регистрация: 8.10.2003 Где: Зеленоград Репутация: 13 Всего: 68 |
TWriter наследник абстрактоного класса TFiler
|
|||
|
||||
Snowy |
|
||||
![]() Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Модератор Сообщений: 11363 Регистрация: 13.10.2004 Где: Питер Репутация: 192 Всего: 484 |
Сейчас поправил - теперь все, как нужно. Добавлено @ 13:52 TComponent это уже более поздняя стадия. Для персистента установка состояния не так актуальна. А для сохранения настроек вообще не имеет значения. |
||||
|
|||||
Alexeis |
|
|||
![]() Амеба ![]() Профиль Группа: Админ Сообщений: 11743 Регистрация: 12.10.2005 Где: Зеленоград Репутация: 109 Всего: 459 |
Еще функция
Это сообщение отредактировал(а) alexeis1 - 2.5.2006, 13:59 -------------------- Vit вечная память. Обсуждение действий администрации форума производятся только в этом форуме гениальность идеи состоит в том, что ее невозможно придумать |
|||
|
||||
Rouse_ |
|
|||
![]() Опытный ![]() ![]() Профиль Группа: Участник Сообщений: 469 Регистрация: 23.4.2005 Репутация: 5 Всего: 29 |
||||
|
||||
Alexeis |
|
|||
![]() Амеба ![]() Профиль Группа: Админ Сообщений: 11743 Регистрация: 12.10.2005 Где: Зеленоград Репутация: 109 Всего: 459 |
Rouse_, глянул в help - действительено, WriteState делает тоже самое, кроме того все объекты имеющие published свойства должны быть предками TComponent иначе на форме их не увидишь.
Мне этот вариант нравится не меньше, надо бы его проработать! -------------------- Vit вечная память. Обсуждение действий администрации форума производятся только в этом форуме гениальность идеи состоит в том, что ее невозможно придумать |
|||
|
||||
Snowy |
|
|||
![]() Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Модератор Сообщений: 11363 Регистрация: 13.10.2004 Где: Питер Репутация: 192 Всего: 484 |
Rouse_, я не спорю, что можно сохранять компоненты.
Любой наследник TPersistent может сохраняться. Для данной цели еще проще воспользоваться методом TStream.WriteComponent. Там собрана примерно та же обертка, что и я нарисовал. Но смысл именно в том, что TComponent нагружает нас кучей ненужных данных и занимает больше памяти. Зачем мне делать компонент для таких вещей, как настройки? Можно конечно для этих целей и TForm приспособить... Статья не про то, как сделать, чтобы меньше ручками набивать. Статья про TPersistent и возможности его применения.
Выход - создание объекта, объединяющего группу объектов в свои property. Второй вариант - искуственная вставка разметки в стрим. Это не сложно, но накладывает ограничение на порядок сохранения и восстановления объектов. |
|||
|
||||
Alexeis |
|
|||
![]() Амеба ![]() Профиль Группа: Админ Сообщений: 11743 Регистрация: 12.10.2005 Где: Зеленоград Репутация: 109 Всего: 459 |
В том то и дело что не очень то удобно! Перед записью каждого компонента необходимо вызвать RegisterClass с параметром имени класса! Куда удобней использовать один writer для записи любых объектов, но вот проблема как их простым путем записать в один поток, может Stream.CopyFrom(); - и передавать кажды раз свой объект потока для каждого объекта и писать перед этим размер этого потока первым полем(чтоб потом в этои же порядке извлкать). -------------------- Vit вечная память. Обсуждение действий администрации форума производятся только в этом форуме гениальность идеи состоит в том, что ее невозможно придумать |
|||
|
||||
Snowy |
|
|||
![]() Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Модератор Сообщений: 11363 Регистрация: 13.10.2004 Где: Питер Репутация: 192 Всего: 484 |
alexeis1, это и есть второй вариант. Ограничение - порядок сохранения и восстановления должен быть одинаковым.
|
|||
|
||||
McDevil |
|
|||
![]() Опытный ![]() ![]() Профиль Группа: Участник Сообщений: 285 Регистрация: 8.12.2005 Где: Казахстан, Павлод ар Репутация: 1 Всего: 3 |
Snowy, хорошая статья!
-------------------- мы знаем столько, сколько можем, а можем столько, сколь хотим... Тестируем программу: SPL-программа аналогов функций |
|||
|
||||
Snowy |
|
|||
![]() Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Модератор Сообщений: 11363 Регистрация: 13.10.2004 Где: Питер Репутация: 192 Всего: 484 |
Хочу сделать еще одно замечание по поводу механизма сохранения компонентов.
У него есть еще один недостаток. Пример:
к примеру, мы сохранили. Можем прочитать. Но! Допустим мы изменили класс. Убрали color. При загрузке возникнет исключение. Допустим, что мы обработаем это исключение. Тогда как загрузится объект? Загрузится x, потом y. На color возникнет исключение. Всё. txt не загрузится. В моей схеме обрабатывается не все целиком, а каждая пропертя отдельно. Поэтому загрузка будет такой: Загрузится x, потом y. На color возникнет исключение - пропускаем. Дальше читаем txt. |
|||
|
||||
Teran |
|
|||
![]() Опытный ![]() ![]() Профиль Группа: Участник Сообщений: 590 Регистрация: 9.9.2005 Где: Украина, Запорожь е Репутация: нет Всего: 3 |
У меня есть один вопросик поповоду этого
Почему не получается сохранить в файл объект TBitBtn
выскакивающий messagebox будет постоянно пустым caption Tbitbtn-а в файле не сохраняется Это сообщение отредактировал(а) Teran - 19.9.2006, 18:34 -------------------- Ни цего не понимаю |
|||
|
||||
Teran |
|
|||
![]() Опытный ![]() ![]() Профиль Группа: Участник Сообщений: 590 Регистрация: 9.9.2005 Где: Украина, Запорожь е Репутация: нет Всего: 3 |
я так понял что можно сохранять только "Стандартные типы" (integer,string,boolean), а сохранение всего остального необходимо реализовывать вручную, основываясь на стандартные типы.
Токда ответте какая из такого сохранения выгода..? если можно тоже самое сделать например спомощью ini файла или тогоже реестра? -------------------- Ни цего не понимаю |
|||
|
||||
Snowy |
|
|||
![]() Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Модератор Сообщений: 11363 Регистрация: 13.10.2004 Где: Питер Репутация: 192 Всего: 484 |
||||
|
||||
Teran |
|
|||
![]() Опытный ![]() ![]() Профиль Группа: Участник Сообщений: 590 Регистрация: 9.9.2005 Где: Украина, Запорожь е Репутация: нет Всего: 3 |
-------------------- Ни цего не понимаю |
|||
|
||||
Snowy |
|
|||
![]() Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Модератор Сообщений: 11363 Регистрация: 13.10.2004 Где: Питер Репутация: 192 Всего: 484 |
Ничего не могу поделать - не сохраняются контролы.
Если вместо батона прописать свой класс - наследник TPersistent с нужными пропертями - сохраняется без проблем. Контролы нет. Нужно создавать свой класс и делать ему инкапсуляцию на вызов функций, создающих нужные контролы. Добавлено @ 10:41 Как будет время, я ещё поковыряю на тему, чего это контролы не хотят сохраняться. Они ведь тоже наследники TPersistent и прекрасно сохраняются, как контролы. Вероятно разница в уровнях. Но пока точно не скажу. |
|||
|
||||
EvilsInterrupt |
|
|||
Executables research ![]() ![]() ![]() Профиль Группа: Завсегдатай Сообщений: 1019 Регистрация: 14.7.2007 Где: Железнодорожный, МО, Россия Репутация: 3 Всего: 9 |
Просмотрел мельком посты, вроде ссылки нету. Ссылка что я хочу предложить весьма финоменальна : Сериализация объектов стандартными средствами Delphi.
|
|||
|
||||
Denchik |
|
|||
Шустрый ![]() Профиль Группа: Участник Сообщений: 68 Регистрация: 16.5.2006 Репутация: нет Всего: нет |
есть еще интересная вещь FormPersist
сохраняет/загружает настройки всех компонентов формы ( форм ) в ini файл баг такой же как в 1 примере данного обсуждения ( http://snowy.delphist.com/mat/TPersistent.zip) не запоминает корректно размеры окна , если запускать его повторно без изменений , двигает его понемногу по диагонали вниз , никто не в курсе , как это лечить ? хочу прикрутить подобный код к сохранению настроек приложения .... Присоединённый файл ( Кол-во скачиваний: 28 ) ![]() |
|||
|
||||
Denchik |
|
|||
Шустрый ![]() Профиль Группа: Участник Сообщений: 68 Регистрация: 16.5.2006 Репутация: нет Всего: нет |
Однако EhLib вроде в самом деле рулит
Используем так :
IniPropStorageMan := TIniPropStorageManEh.Create(nil); IniPropStorageMan.IniFileName := ExtractFileDir(ParamStr(0)) + '\Main.Ini'; SetDefaultPropStorageManager(IniPropStorageMan); И радуемся жизни ![]() |
|||
|
||||
![]() ![]() ![]() |
Правила форума "Delphi: Общие вопросы" | |
|
Запрещается! 1. Публиковать ссылки на вскрытые компоненты 2. Обсуждать взлом компонентов и делиться вскрытыми компонентами
Если Вам понравилась атмосфера форума, заходите к нам чаще! С уважением, Snowy, MetalFan, bems, Poseidon, Rrader. |
0 Пользователей читают эту тему (0 Гостей и 0 Скрытых Пользователей) | |
0 Пользователей: | |
« Предыдущая тема | Delphi: Общие вопросы | Следующая тема » |
|
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности Powered by Invision Power Board(R) 1.3 © 2003 IPS, Inc. |