На главную Наши проекты:
Журнал   ·   Discuz!ML   ·   Wiki   ·   DRKB   ·   Помощь проекту
ПРАВИЛА FAQ Помощь Участники Календарь Избранное RSS
msm.ru
! ПРАВИЛА РАЗДЕЛА · FAQ раздела Delphi · Книги по Delphi
Обязательно выделяйте текст программы тегом [сode=pas] ... [/сode]. Для этого используйте кнопку [code=pas] в форме ответа или комбобокс, если нужно вставить код на языке, отличном от Дельфи/Паскаля.

Этот раздел предназначен для вопросов, посвященных разработке компонентов, а также для тестирования собственных бесплатных компонентов с открытым исходным кодом.

Здесь запрещается:
1. Размещать ссылки на какие-либо коммерческие компоненты, реализующие требуемую функциональность.
2. Обсуждать и тестировать коммерческие компоненты или компоненты с закрытым кодом.
3. Давать ссылки на сайты с исходным кодом компонентов. Все тестируемые исходные коды должы быть размещены на сайте ИСХОДНИКИ.RU.
Модераторы: Rouse_, DimaBr
  
> Как уничтожить TDatamodule
    Решил создать для себя класс для того чтобы скрыть в приложении детали соединения и общения с базой. Для этого создаю :
    ExpandedWrap disabled
      unit baseconnector;
       
      interface
      uses rwindows,forms,sysutils,classes,IBDatabase, DB, IBCustomDataSet, IBQuery,
      datamodule1 ;
      type
       TBaseConnector= class
       
      private
       
      public
      app2:TApplication;
      constructor Create(app:TApplication);
       destructor Destroy; override;
      function getallparameters(groupid:integer;parnamelist,parvallist,pargrouplist:tstringlist):integer;
       
      end;
       
      implementation
       
       constructor TBaseConnector.Create(app:TApplication);
       begin
         app.CreateForm(TDataModule2, DataModule2);
         app2:=app;
       end;
       
       destructor TBaseConnector.Destroy;
      begin
      //dATAMODULe2.DestroyComponents;
      //  Datamodule2.Free;//ошибка Invalid Pointer Operation
       
       
       
        inherited;
      end;
       
       
       
      end.


    Не знаю, может делаю через задницу но для того чтобы не париться с ручным созданием IBDatabase, Ibtransaction и т.д ... решил использовать tdatamodule чтобы накидать туда компонентов а его уже юзать через TBaseConnector.

    ExpandedWrap disabled
      unit datamodule1;
       
      interface
       
      uses
        SysUtils, Classes, IBDatabase, DB, IBCustomDataSet, IBQuery;
       
      type
        TDataModule2 = class(TDataModule)
          IBDatabase1: TIBDatabase;
          IBTransaction1: TIBTransaction;
          IBQuery1: TIBQuery;
          IBDataSet1: TIBDataSet;
        private
          { Private declarations }
        public
          { Public declarations }
        
        end;
       
      var
        DataModule2: TDataModule2;
       
      implementation
       
      {$R *.dfm}
       
       
       
      end.

    Вопрос в том как уничтожить экземпляр Datamodule2?
      TBaseConnector сделай интерфейсом, в дата модуле реализуй его...
        Цитата cyberovskij @
        Datamodule2.Free;//ошибка Invalid Pointer Operation

        Потому что он в этот момент уже уничтожен.
        1) для того что бы узнать что компонент уничтожается у TCompenent есть событие Notification
        2) компонент автодеструктурируется если уничтожается его владелец. (в данном случаи его владельцем будет app: TApplication).
        3) нафзачем тебе переменная app2:TApplication? то есть ты не можешь заглянуть в модуль forms и взять ее там?
          Цитата ViktorXP @
          3) нафзачем тебе переменная app2:TApplication? то есть ты не можешь заглянуть в модуль forms и взять ее там?

          А хр не знаю
            Цитата cyberovskij @
            Не знаю, может делаю через задницу но для того чтобы не париться с ручным созданием IBDatabase, Ibtransaction и т.д ... для того чтобы скрыть в приложении детали

            Не сомневайся, именно так ты и поступаешь.
            Ну и что такого сложного: задать имя базы данных.
              Цитата 05772 @
              Ну и что такого сложного: задать имя базы данных.

              Может и действительно ничего сложного для ( нескольких_database+ нескольких_ibtransaction+ нескольких_десятков_датасетов)*нескольких_десятков_дефолтных_значений.
                А зачем уничтожать датамодуль, скажи, пожалуйста. Сильно мешает?
                  Цитата Fr0sT @
                  А зачем уничтожать датамодуль, скажи, пожалуйста. Сильно мешает?

                  А зачем освобождают ненужные ресурсы?
                    cyberovskij так он у тебя нормально при выгрузке приложения освободится и без твоего участия. Зачем это делать руками?
                      Но если надо ручками тоже можно. А почему бы и нет?
                      Надо просто отслежитвать корректно освобождение "старндартной" var DataModule2.
                      Например, таким макаром:
                      ExpandedWrap disabled
                        unit datamodule1;
                         
                        type
                           TDataModule2 = class   // (?) странно что в Unit "DataModule1" находится TDataModule2, может быть их сделать с одинаковыми номерами?
                           ...
                           public
                           ...
                               destructor Destroy; override;
                           end;
                        ...
                         
                        var
                          DataModule2: TDataModule2;
                         
                        implemenation
                        ...
                         
                        destructor TDataModule2.Destroy;
                        begin
                           if Self = DataModule2 then DataModule2 := nil;
                           inherited;
                        end;
                         
                        ...
                        end.

                      а освообождение выполнять не "диким" способом

                      ExpandedWrap disabled
                         DataModule2.Free;
                      :o :no:

                      а аккуратно:
                      ExpandedWrap disabled
                         FreeAndNil(DataModule2);

                      чтобы в случае, когда DataModule2 уже почил, не получить "NULL Pointer Exception"
                      ;)
                        Цитата RuSA @
                        Надо просто отслежитвать корректно освобождение "старндартной" var DataModule2.

                        более безопаснее будет заюзать Notification. да и более удобно. можно выполнить какое то действие в сам момент уничтожения
                          Если юзать FreeAndNil, то if Self = DataModule2 then DataModule2 := nil; - излишне
                            Совсем нет.

                            Цитата Fr0sT @
                            Если юзать FreeAndNil, то if Self = DataModule2 then DataModule2 := nil; - излишне

                            Наличие проверки в деструкторе, гарантирует очистку становящейся некорректной var-переменной. Ведь никто не гарантирует, что все виды освобождений будут проходить именно через FreeAndNil !?

                            Цитата ViktorXP @
                            более безопаснее будет заюзать Notification. да и более удобно. можно выполнить какое то действие в сам момент уничтожения

                            Интересно чем безопаснее?
                            Завязка на Notification нужна там, где вводится синоним. Прич1м изящно это можно сделать, если класс обладатель синонима будет потомком TComponent (у которого есть Notification).

                            Т.е. если бы в модуле TBaseConnector была своя копия указателя, то тогда надо было бы иметь Notification.
                            Т.е. примерно так:
                            ExpandedWrap disabled
                              type
                                 TBaseConnector = class(TComponent)
                                 ...
                                 private
                                    FDM: TDataModule2;
                                    ...
                                    procedure setDM( adm: TDataModule2);
                                    ...
                                 protected    
                                    ...
                                    procedure Notification(AComponent: TComponent; Operation: TOperation); override;
                                 ...
                                 public
                                    property DM: TDataModule2 read FDM write setDM;
                                 end;
                               
                              implementation
                               
                              ...
                               
                              procedure TBaseConnector.Notification(AComponent: TComponent; Operation: TOperation);
                              begin
                                 inherited;
                                 if Operation = opRemove then
                                 begin
                                    if AComponent = Self.FDM then Self.FDM := nil;
                                 end;
                              end;
                               
                              procedure TBaseConnector.setDM( adm: TDataModule2);
                              begin
                                 if (FDM <> adm) then
                                 begin
                                    FDM := adm;
                                    if (adm <> nil) then adm.FreeNotofication(Self);
                                 end;
                              end;
                               
                              ...
                              end.

                            Такой метод применять для самого TDataModule очевидно, слишком длино.
                            В деструкторе это смотрится гораздо логичнее, тем более что DataModule2 это unit-перменная, а не поле экземпляра класса (instance-field).
                              Цитата RuSA @
                              Интересно чем безопаснее?

                              никогда не нарвешся на мертвую ссылку. и тогда даже задумываться не будешь насчет
                              Цитата RuSA @
                              Наличие проверки в деструкторе, гарантирует очистку становящейся некорректной var-переменной. Ведь никто не гарантирует, что все виды освобождений будут проходить именно через FreeAndNil !?


                              пс. и его вообще можно конструировать в этом контейнере по запросе.
                                Честно говоря думая что я не докнца понял что Вы хотели сказать, но вот есть такие соображения:
                                1) FreeAnlNil(X) удобнее простого X.Free вот чем:
                                а) если освобождаемая переменная X уже nil, то FreeAndNil выполнится без ошибок, а Free даст "NULL Pointer",
                                б) после выполнения освобождения, надо очичать X, иначе там останется "битая ссылка". FreeAndNil после своего выполнения выставит nelf nil, Free - нет.
                                в) если перед выполнением операций в X уже ссылка битая (т.е. X <> nil, но сама перменная освобождена), оба варианта дадут нерпедсказуемые проблемыи в этом они схожи.

                                2) по-поводу
                                никогда не нарвешся на мертвую ссылку

                                Цитата ViktorXP @
                                никогда не нарвешся на мертвую ссылку

                                это слишком громко сказано, т.к. в достаточно большом или старом проекте, нельзя гарантировать, что использования всегда будут "академически правильными". Так что простые "FreeAnNil", как показывает практика, достаточно просто позволят локализовать подобные ошибки.
                                И вопрос корректного отслеживания ссылок в языках с отсутствием встроенного "сборщика мусора" вообще большая проблема.
                                Вариант с обычным Notification проходит удобно только для синонимов внутри классов-потомков TCompoment, для других классов надо в каждом случае думать как это сделать безопасно.

                                В исходном варианте проблема была в том, что в момент вызова
                                ExpandedWrap disabled
                                   Datamodule2.Free;//ошибка Invalid Pointer Operation
                                Datamodule2 была <> nil, но сам экземпляр уже был освобождён. Так что вариант с обнулением в деструкторе вылечил бы это, если бы тут использовалось FreeAndNil.
                                Вот такие соображения.
                                  Цитата RuSA @
                                  это слишком громко сказано, т.к. в достаточно большом или старом проекте, нельзя гарантировать, что использования всегда будут "академически правильными".

                                  Вот исходя из опыта как раз работы с большим проектом я тебе и говорю что нотификация спасет мир. а глобальные переменные нужно сводить к минимуму.

                                  Добавлено
                                  пс. на моей практике я несколько подобных случаев (которых предлагаеш ты) и справлял на нотификацию. я бы не лез (ибо старые проекты лучше не трогать пока они работают) но контейнеров создавалось несколько и каждый создавал для себя дата модуль (при этом глобальная переменная юзалась одна. что тоже являлось ошибкой ибо они повисали в воздухе), и при уничтожении одного из модулей все остальные контейнеры не могли уже работать ибо переменная nil.
                                  посему повторюсь что нотификация спасет мир. еще ни разу не подводила.
                                    ExpandedWrap disabled
                                      function DataModule1: TDataModule1;
                                      procedure DestroyDataModule1;
                                       
                                      implementation
                                       
                                      var dmDataModule1: TDataModule1;
                                       
                                      function DataModule1: TDataModule1;
                                      begin
                                        if dmDataModule1 = nil then
                                          dmDataModule1 := TDataModule1.Create;
                                        Result := dmDataModule1;
                                      end;
                                       
                                      procedure DestroyDataModule1;
                                      begin
                                        FreeAndNil(dmDataModule1);
                                      end;


                                    А вообще я продолжаю не понимать смысл затеи. Для освобождения памяти достаточно деактивировать все объекты модуля, зачем его сам-то сносить?
                                      Цитата Fr0sT @
                                      Для освобождения памяти достаточно деактивировать все объекты модуля, зачем его сам-то сносить?

                                      да самый простой вариант это авто создание и пусть живет до конца жизни программы. если ничего не трогать в этом механизме то никаких проблем не будет
                                        Цитата RuSA @
                                        а) если освобождаемая переменная X уже nil, то FreeAndNil выполнится без ошибок, а Free даст "NULL Pointer",

                                        Нет, т.к. Free делает проверку if Self <> nil then Destroy;


                                        Цитата RuSA @
                                        В исходном варианте проблема была в том, что в момент вызова
                                        ...
                                        Datamodule2 была <> nil, но сам экземпляр уже был освобождён. Так что вариант с обнулением в деструкторе вылечил бы это, если бы тут использовалось FreeAndNil

                                        Ничего бы он не вылечил, т.к. проблема не в обнулении, а в том, что объект DataModule2 сделан слугой фиг знает скольки господ - и TBaseConnector, и Application, и как глобальная переменная ва-аще доступна всем желающим, в частности шаловливым ручонкам г-на cyberovskij ;) Если бы DataModule2 удалялся один раз в деструкторе TBaseConnector до завершения приложения (например в FormDestroy или раньше), то никакой-бы ошибки тут не было и без обнуления. Но раз ошибка есть, то либо удаление TBaseConnector прописано где-то в finalization и срабатывает после Application.Free, либо автор где-то понатыкал еще несколько удалений. Как бы то ни было, а подход корявый - раз собрался "прятать" компоненты работы с базой, то 1) незачем глобальную переменную создавать, 2) незачем сюда вообще приплетать Application - создавай объект, через TDataModule1.Create(nil) и сам отвечай за его удаление (ну или наоборот - если не нужно удалять "досрочно", то пусть сидит в Application до "конца жизни")
                                        Сообщение отредактировано: leo -
                                          Цитата ViktorXP @
                                          на моей практике я несколько подобных случаев (которых предлагаеш ты) и справлял на нотификацию.

                                          здесь предлагает автор - ему надо освободить объект datamodule2.
                                          Для этого предлагается вызвать элементарное FreeAndNil(DataModule2) в нужном месте проги.
                                          + страховка внутри destroy, для очитски АВТОМАТИЧЕКИ созданной Delphi переменной.
                                          И всё.

                                          Нотификация тут не при чём. Её механизм гораздо более сложный, чем описанное выше. И к тому же, для класса исходного автора её нельзя применить воовсе, т.к. класс не является наследником TComponent.
                                            Цитата RuSA @
                                            здесь предлагает автор - ему надо освободить объект datamodule2

                                            Еще раз - если автор освобождает объект datamodule2 только в деструкторе TBaseConnector и ДО того как этот объект будет уничтожен Application, то никакое обнуление не нужно. Если же ПОСЛЕ, то - "лопух", и никакое обнуление тут тоже не поможет. Если же где-то удаляет "ручками" через явный вызов datamodule.free, то тоже "лопух" - не подумал, к чему это может привести
                                            Сообщение отредактировано: leo -
                                              Цитата leo @
                                              Цитата RuSA @
                                              здесь предлагает автор - ему надо освободить объект datamodule2

                                              Еще раз - если автор освобождает объект datamodule2 только в деструкторе TBaseConnector и ДО того как этот объект будет уничтожен Application, то никакое обнуление не нужно. Если же ПОСЛЕ, то - "лопух", и никакое обнуление тут тоже не поможет. Если же где-то удаляет "ручками" через явный вызов datamodule.free, то тоже "лопух" - не подумал, к чему это может привести

                                              Уже автор давно не освобождает datamodule2. Я принял во внимание топик #5 от 05772 и решил все дата_объекты создавать и уничтожать в модуле baseconnector; всем спасибо за отписку.

                                              Добавлено
                                              Цитата leo @
                                              шаловливым ручонкам г-на cyberovskij

                                              Ага, видели бы мои ручища
                                                Цитата cyberovskij @
                                                Уже автор давно не освобождает datamodule2. Я принял во внимание топик #5 от 05772 и решил все дата_объекты создавать и уничтожать в модуле baseconnector; всем спасибо за отписку.

                                                Молодец, только, боюсь, что ты так толком и не понял, что модуль с TDataModule ничем особым не отличается от обычного модуля и лишь упрощает создание и настройку компонентов БД - все остальное в твоих руках. Убираешь дата-модуль из автосоздания, удаляешь переменную var DataModule2:TDataModule2, добавляешь описание и код твоего класса TBaseConnector и сохраняешь модуль под именем baseconnector. Ну и ес-но вместо Application.CreateForm юзаешь FDm:=TDataModule2.Create(nil), где FDm:TDataModule2 - приватное поле твоего класса. У-се, работай наздоровье с дата_объектами через свой класс - ни Application, никакие другие шаловливые "ручища" их не удалят ;)
                                                Хотя, если хочешь по совету 05772 все своими "ручищами" создавать, то ради бога
                                                Сообщение отредактировано: leo -
                                                0 пользователей читают эту тему (0 гостей и 0 скрытых пользователей)
                                                0 пользователей:


                                                Рейтинг@Mail.ru
                                                [ Script execution time: 0,0578 ]   [ 17 queries used ]   [ Generated: 26.04.24, 12:52 GMT ]