Если говорить о системах с GC, должна ли операция добавления обработчика к событию
b.signal += a.slot
создавать сильную или слабую ссылку на a? (сильная — значит b держит a, пока не сделают -= или их не подберет GC; слабая — при сборке `a` соединение само собой разорвется).
Какие есть большие системы, реализованные обоими способами? Какие достоинства/недостатки у каждого подхода? Особенно интересно, чем каждый подход оборачивается в неидеальных командах, где не все ошибки исключаются на этапе проектирования.
Кё>Если говорить о системах с GC, должна ли операция добавления обработчика к событию
Кё>
Кё>b.signal += a.slot
Кё>
Кё>создавать сильную или слабую ссылку на a? (сильная — значит b держит a, пока не сделают -= или их не подберет GC; слабая — при сборке `a` соединение само собой разорвется).
Мне кажется, что здесь общего принципа быть не может. Основной вопрос — если слот завершается или должен быть отключен, кто именно это контролирует? Если предусмотрен явный контроль этого (и он надёжен), можно использовать сильную ссылку. Если нужно явно вводить логику "не отключать до красного свистка" — то же самое. Но если контроля нет или есть настроение поставить вопрос "на самотёк" — можно использовать слабые, только не забывать их удалять (если автоудаления нет).
Достаточно чёткий случай для слабой ссылки был у меня в B2BUA. К менеджеру транзакций подписываются получатели запросов с конкретным call-id. Получатели — объекты диалогов. "Сигналы" соответствуют call-id'ам. Если диалог завершается, кто-то должен вынести его из списка подписчиков call-id. Структура этого всего — словарь с элементами — списками ссылок. Если ссылка сильная и что-то в удалении диалога происходит не так — диалог не доходит до кода выноса себя из подписки, и остаётся постоянная ссылка на него. Слабые ссылки тут пока не сделал, но планирую: что бы ни происходило при завершении диалога, он не останется висеть в памяти.
(К слову, "что бы ни происходило" тут — ключевой элемент: программа строится так, чтобы всевозможные ошибки, которые таки будут, ломали максимум тот звонок, в котором они сработали. На одновременные с этим звонки и последующие не должно быть влияния. Поэтому, накопление "мусора" тут недопустимо.)
Кё>Какие есть большие системы, реализованные обоими способами?
Кё>Если говорить о системах с GC, должна ли операция добавления обработчика к событию
Кё>
Кё>b.signal += a.slot
Кё>
Кё>создавать сильную или слабую ссылку на a? (сильная — значит b держит a, пока не сделают -= или их не подберет GC; слабая — при сборке `a` соединение само собой разорвется).
Я, может быть, чего не понял. А откуда тут вообще ссылка на 'a'? В сигнал добавляется ф-ия (или что там у вас) slot, и на нее и будет ссылка. Если slot-у нужен 'a' (есть ссылка в замыкании, или чем у вас там является slot), то и на него будет ссылка через slot.
В дотнете по дефолту используются сильные ссылки. Как следствие — можно получить "утечку" в виде объекта, подписанного на событие и никаким другим способом не используемого.
Здравствуйте, vshabanov, Вы писали:
Кё>>b.signal += a.slot V>Я, может быть, чего не понял. А откуда тут вообще ссылка на 'a'?
В терминах C++ a.slot эквивалентно паре &A::slot() и ссылке на `a` (чтобы было что дать в качестве this при вызове). На C# это был бы делегат, который внутри точно также сохраняет ссылку на объект и какой метод вызывать.
Здравствуйте, Mr.Cat, Вы писали:
MC>В дотнете по дефолту используются сильные ссылки. Как следствие — можно получить "утечку" в виде объекта, подписанного на событие и никаким другим способом не используемого.
ИМХО, не так уж и сложно не наступать на эти грабли.
Help will always be given at Hogwarts to those who ask for it.
Кё>Если говорить о системах с GC, должна ли операция добавления обработчика к событию
Кё>b.signal += a.slot
Кё>создавать сильную или слабую ссылку на a? (сильная — значит b держит a, пока не сделают -= или их не подберет GC; слабая — при сборке `a` соединение само собой разорвется).
Мне кажется, что определять это должен подписчик в момент подписки. Он должен уметь (в "хороших", по моему мнению, системах) сам выбрать и решить, как должна работать создаваемая им связь.
Кё>Какие достоинства/недостатки у каждого подхода?
Тут нельзя говорить о "достоинствах-недостатках". В некоторых случаях удобнее одно, в других — другое.
Help will always be given at Hogwarts to those who ask for it.
Здравствуйте, Кодёнок, Вы писали:
Кё>создавать сильную или слабую ссылку на a? (сильная — значит b держит a, пока не сделают -= или их не подберет GC; слабая — при сборке `a` соединение само собой разорвется).
Кё>Какие есть большие системы, реализованные обоими способами? Какие достоинства/недостатки у каждого подхода? Особенно интересно, чем каждый подход оборачивается в неидеальных командах, где не все ошибки исключаются на этапе проектирования.
Это сильно зависит от того, что ты строишь. Как водится, одного решения на все случаи жизни нету.
Имхо, нормальные библиотеки должны предоставлять оба способа, потому что в разных случаях нужно разное.
Частенько бывает удобно создать объект только для того, чтобы отдать его в спамеру и забыть о нем, и чтоб дальше он работал самостоятельно и умер, когда умрет спамер — в этом случае сильная ссылка как раз то, что нужно.
Ближайший пример — прогресс-бар: создал, отдал и забыл.
А бывает и наоборот, естественно.