Передача интерфейсов как параметров функций

Общие вопросы программирования, алгоритмы и т.п.

Модератор: Модераторы

Передача интерфейсов как параметров функций

Сообщение java73 » 10.11.2020 16:03:52

Объясните мне, дураку, чем передача объектов в качестве параметров функций отличается от передачи интерфейсов (ну, фактически, объектов, реализующих эти интерфейсы).
К примеру была сигнатура такая:
Код: Выделить всё
procedure Execute(const Command: string; Data: TVisited; Context: TContext);

В него последним передавался объект типа TMyContext - наследник TContext. И там с ним внутри все работало, почему бы и нет. Но в паскале из-за наличия жесткого принципа нежелательности взаимных ссылок двух модулей друг на друга (в uses), да и в общем смысле из-за нарушения принципа инверсии зависимостей я все думал, как мне избавиться от этой проблемы.
Вынес Context в интерфейс. Переписал сигнатуру метода следующим образом:
Код: Выделить всё
procedure Execute(const Command: string; Data: TVisited; Context: IInterface);

Однако передавая в этот метод объект, реализующий интерфейс IContext (который по умолчанию наследник IInterface = IUnknown), ссылка на него сразу пропадает. При отладке тупо складывается впечатление, что ссылка на этот объект просто где-то теряется, но где, я понять не могу пока что.
Может кто здесь поможет разобраться?
java73
постоялец
 
Сообщения: 253
Зарегистрирован: 21.11.2013 09:08:10

Re: Передача интерфейсов как параметров функций

Сообщение iskander » 10.11.2020 18:10:16

А если объявить так
Код: Выделить всё
procedure Execute(const Command: string; Data: TVisited; Context: IContext);
?
iskander
постоялец
 
Сообщения: 382
Зарегистрирован: 08.01.2012 18:43:34

Re: Передача интерфейсов как параметров функций

Сообщение runewalsh » 10.11.2020 18:45:27

Присваивание созданного объекта интерфейсной ссылке увеличивает его счётчик ссылок с 0 до 1. Когда процедура завершается, ссылка в IContext-параметре финализируется, счётчик уменьшается до 0 и, видя это, объект уничтожается. Поэтому одно из двух:

— Объект, который ты используешь с интерфейсами, ты должен сразу после создания присвоить интерфейсной переменной и в дальнейшем работать только через интерфейсы, чтобы гарантировать ненулевой счётчик ссылок;

— Хотя бы передавай через const-параметр: procedure Execute(... const Context: IContext). Const-параметр не трогает счётчик ссылок.

По-хорошему второй вариант не должен работать: если мы передаём TContext как IContext, компилятор всё равно должен бы создать временную переменную типа IContext, присвоить ей TContext с бампом счётчика ссылок, передать её как параметр, а по возвращении из процедуры финализировать временную IContext с декреметом счётчика и, при достижении 0, уничтожением объекта TContext. Но в Паскале принята багофича, что всего этого не выполняется. Из-за этого код F(TMyObject.Create), где procedure F(const o: IMyObject), приведёт к утечке памяти — созданный объект не будет уничтожен, а с F(o: IMyObject) — будет.
Аватара пользователя
runewalsh
постоялец
 
Сообщения: 498
Зарегистрирован: 27.04.2010 00:15:25

Re: Передача интерфейсов как параметров функций

Сообщение iskander » 10.11.2020 19:48:40

Честно говоря, мне казалось, что передача ARC-интерфейса как параметра корректно работает во всех случаях.
Но в случае const есть надёжный способ получить утечку памяти:
Код: Выделить всё
  MyCoolMethod(TMyCoolIntfClass.Create);
iskander
постоялец
 
Сообщения: 382
Зарегистрирован: 08.01.2012 18:43:34

Re: Передача интерфейсов как параметров функций

Сообщение runewalsh » 10.11.2020 21:15:09

Фишка в том, что если тебе очень надо воспользоваться интерфейсами именно как наборами методов, без фокусов с ARC, то через const ты сможешь это сделать. Утечка при неправильном использовании мгновенно обнаруживается (код
Код: Выделить всё
var
   x: TMyObject;
begin
   x := TMyObject.Create;
end;
— тоже утечка, и что теперь, не использовать Create?), а вот то, что передача по значению будет медленнее раз так в 20 (атомарные инкремент и декремент vs ничего) — не обнаружится.
Аватара пользователя
runewalsh
постоялец
 
Сообщения: 498
Зарегистрирован: 27.04.2010 00:15:25

Re: Передача интерфейсов как параметров функций

Сообщение iskander » 10.11.2020 21:20:15

Полагаю ТС это по барабану.
iskander
постоялец
 
Сообщения: 382
Зарегистрирован: 08.01.2012 18:43:34

Re: Передача интерфейсов как параметров функций

Сообщение runewalsh » 10.11.2020 22:11:40

А, есть ещё третий вариант —
Код: Выделить всё
{$interfaces corba}
Интерфейсы, объявленные в этом режиме, не наследуются от IUnknown и не ARC'аются. Это локальная директива, так что можно включить её для конкретных интерфейсов.
Аватара пользователя
runewalsh
постоялец
 
Сообщения: 498
Зарегистрирован: 27.04.2010 00:15:25

Re: Передача интерфейсов как параметров функций

Сообщение iskander » 10.11.2020 22:23:12

Не въехал, где второй?
iskander
постоялец
 
Сообщения: 382
Зарегистрирован: 08.01.2012 18:43:34

Re: Передача интерфейсов как параметров функций

Сообщение runewalsh » 10.11.2020 23:56:04

runewalsh писал(а):Поэтому одно из двух:
Аватара пользователя
runewalsh
постоялец
 
Сообщения: 498
Зарегистрирован: 27.04.2010 00:15:25

Re: Передача интерфейсов как параметров функций

Сообщение iskander » 11.11.2020 08:52:27

Омг, извини, невнимательно читал.
iskander
постоялец
 
Сообщения: 382
Зарегистрирован: 08.01.2012 18:43:34

Re: Передача интерфейсов как параметров функций

Сообщение java73 » 11.11.2020 10:41:49

runewalsh писал(а):— Объект, который ты используешь с интерфейсами, ты должен сразу после создания присвоить интерфейсной переменной и в дальнейшем работать только через интерфейсы, чтобы гарантировать ненулевой счётчик ссылок


Спасибо большое. Вот это помогло начать думать в нужную сторону.
У меня контекст - синглтон, в модуле, где он объявлен, существует в качестве глобальной переменной. Его тип сначала написал не как интерфейс, а как класс, который этот интерфейс реализовывает. И это видимо имеет существенное значение.
Кстати, обратил внимание, что встроенный модуль проверок утечки памяти вроде как глючит: при завершении приложения этот объект высвобождается и его деструктор точно отрабатывает (ставил точку остановки), т.е. все созданные внутри списки с объектами тоже высвобождаются. Однако при простом проходе деструктора модуль утечек памяти это не видит, если же все то же, что делает деструктор, поместить в другую функцию интерфейса, например, release, то он это видит и утечек не находит (при этом, деструктор самого класса все равно тоже вызывается).
java73
постоялец
 
Сообщения: 253
Зарегистрирован: 21.11.2013 09:08:10

Re: Передача интерфейсов как параметров функций

Сообщение Pavia » 11.11.2020 13:07:58

— Объект, который ты используешь с интерфейсами, ты должен сразу после создания присвоить интерфейсной переменной и в дальнейшем работать только через интерфейсы, чтобы гарантировать ненулевой счётчик ссылок;

Считаю данное поведение ошибкой. Поэтому вручную увеличиваю счетчик ссылок в конструкторе и уменьшаю в деструкторе.
Аватара пользователя
Pavia
постоялец
 
Сообщения: 281
Зарегистрирован: 07.01.2011 12:46:51

Re: Передача интерфейсов как параметров функций

Сообщение runewalsh » 11.11.2020 14:12:39

Pavia писал(а):вручную увеличиваю счетчик ссылок в конструкторе и уменьшаю в деструкторе

Ах да, этого не нужно делать, ЧЕТВЁРТЫЙ вариант:

— Сделать свой аналог TInterfacedObject, в котором QueryInterface реализована так же, а методы _AddRef и _Release просто возвращают -1, и наследовать объекты от него. Встроенного аналога будто бы нет, хотя не уверен. В Delphi он называется TSingletonImplementation.
Аватара пользователя
runewalsh
постоялец
 
Сообщения: 498
Зарегистрирован: 27.04.2010 00:15:25

Re: Передача интерфейсов как параметров функций

Сообщение iskander » 11.11.2020 14:17:48

А зачем, если есть третий?
iskander
постоялец
 
Сообщения: 382
Зарегистрирован: 08.01.2012 18:43:34

Re: Передача интерфейсов как параметров функций

Сообщение runewalsh » 11.11.2020 15:18:04

Третий несовместим с Delphi :D
Собственно, именно такой способ отключения автоматического подсчёта ссылок — заглушки вместо _AddRef и _Release — используется в TComponent.
Аватара пользователя
runewalsh
постоялец
 
Сообщения: 498
Зарегистрирован: 27.04.2010 00:15:25


Вернуться в Общее

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 3

Рейтинг@Mail.ru