Неочевидные проблемы использования объектов.

atv

Новичок
Неочевидные проблемы использования объектов.

Как известно (http://www.php.net/manual/en/language.oop5.basic.php), объекты в PHP5 освобождаются из памяти только тогда, когда больше нет ссылок указывающих на объект. Так вот, это приводит к тому, что объекты с взаимными ссылками практически невозможно освободить из памяти.
Как видно из следующего примера
PHP:
class Simple1
{
    protected $simple2;

    public function __construct()
    {
        $this->simple2 = new Simple2($this);
    }

    public function __destruct()
    {
        print '<br>Destruct Simple1';
    }
}

class Simple2
{
    protected $simple1;

    public function __construct($simple1)
    {
        $this->simple1 = $simple1;
    }

    public function __destruct()
    {
        print '<br>Destruct Simple2';
    }
}

$simple1 = new Simple1();

unset($simple1);

print '<br>End of script';
Объекты не уничтожаются после вызова unset($simple1).
Код:
End of script 
Destruct Simple1
Destruct Simple2
Ручной вызов метода __destruct() также не приводит к освобождению памяти, поэтому остаётся единственный вариант
PHP:
class Simple1
{
    [...]
    
    // добавить в класс специальный метод...
    public function free()
    {
        unset($this->simple2);
    }
}

$simple1 = new Simple1();

// и вызывать его...
$simple1->free();

// а потом уже...
unset($simple1);
В этом случае
Код:
Destruct Simple2
Destruct Simple1
End of script
Вот такие пироги...
 

Гравицапа

elbirret elcno
atv
1. Боян вроде как
2. Реальная ситуация, при которой такое (объекты с взаимными ссылками) может понадобиться?
 

Gorynych

Посетитель PHP-Клуба
Автор оригинала: Гравицапа
...
2. Реальная ситуация, при которой такое (объекты с взаимными ссылками) может понадобиться?
вот такой пример частого взаимообращения объектов разных классов друг к другу будет в тему? Есть класс, описывающий (реализующий) сеанс , и класс, описывающий (реализующий) окружение приложения. Правда у меня в обоих этих случаях оба класса - одиночки и взаимодействуют через вызовы статических методов.
 

Gorynych

Посетитель PHP-Клуба
jonjonson спорный момент. Я считаю, что класс реализующий окружение приложения фактически выполняет роль мини-загрузчика, а класс реализующий работу сеансов (в моем случае) работает только тогда, когда активна сессия (а так бывает не всегда). Поэтому осознано разделял их.

сразу отвечу на незаданный вопрос: зачем там взаимные вызовы? Ну вызовы к классу окружения приложения понятны, а он, в свою очередь, проверяет, не изменялись ли базовые настройки в данном конкретном сеансе. Где-то так.
 

atv

Новичок
2. Реальная ситуация, при которой такое (объекты с взаимными ссылками) может понадобиться?
Кольцо может получиться из большего числа объектов: A->B->C->A и множество других вариантов, в которых может получиться замкнутое кольцо.
 

Gorynych

Посетитель PHP-Клуба
atv ...что, в общем-то, при вызове не статичных методов должно сигнализировать/предупреждать о возможных узких / потенциально опасных местах в иерархии классов
 

grigori

( ͡° ͜ʖ ͡°)
Команда форума
встроенной защиты так же нет от бесконечного цикла, рекурсии и людской глупости
 

bkonst

.. хочется странного?...
2. Реальная ситуация, при которой такое (объекты с взаимными ссылками) может понадобиться?
Ляхко. Дерево со ссылками на узел-родитель. Двусвязный список. Обе структуры абсолютно типовые.
 

jonjonson

Охренеть
bkonst, возможно определённые структуры должны иметь определённые методы удаления элементов из себя? atv привёл пример с методом free().
Однако акцент сделан на примере не осознанного зацикливания ссылок. Не осознанность в данном случае - не продуманность архитектуры.
 

bkonst

.. хочется странного?...
Тут дело не в удалении элементов, а в том что при выходе из области видимости такая структура целиком может остаться болтаться в памяти; например, новичок (не знающий о поведении GC в PHP) вполне может написать такой код:
PHP:
function foo() {
  // Делаем маленькое дерево с осознанно зацикленными 
  // ссылками
  $node = new TreeNode();
  $node->addChild(new TreeNode());

  ....

  // тут мы наивно полагаем, что память, занятая $node 
  // и его потомками, будет освобождена
}
Об этом надо помнить и явно учитывать в коде. Понятно, что Garbage Collector, корректно работающий с циклическими структурами, будет сложнее и медленнее; однако предупреждение о граблях такого рода вполне могло бы быть и в официальной документации.

(Плохо даже не то, что деструктор не будет вызван в момент завершения функции - этим страдают большинство языков с GC; плохо то, что он не будет вызван никогда).
 

jonjonson

Охренеть
bkonst, и где в вашем коде петля из ссылок?
И насчёт освобождения памяти... Где ваш комментарий она вовсе не должна быть освобождена от $node. Комментарий должен быть за скобкой...
 

bkonst

.. хочется странного?...
Мэ, ну не надо придираться к техническим деталям ;) Мы же оба понимаем, что на самом деле имеется в виду. Суть в том, что прострелить себе ногу гораздо легче, чем кажется.

Петля подразумевается, так как выше говорилось о деревьях специфического вида; создавалась бы она внутри addChild(). Например.

Положение комментария за скобкой, хотя и "более правильное", но неудобное с точки зрения чтения кода. Большая часть чтецов отнесет такой комментарий к функции, следующей за foo.
 

atv

Новичок
что, в общем-то, при вызове не статичных методов должно сигнализировать/предупреждать о возможных узких / потенциально опасных местах в иерархии классов
Да ничем не просигнализируеш. Это уже на стадии оптимизации нужно проверить расход памяти, или подсчитать количество созданных обеъктов, если при завершении приложения их неоправдано много, значит проблемы...
 

hermit_refined

Отшельник
наличие циклических ссылок - в контексте php - является следствием ошибок проектирования.
даже тогда, они действительны нужны, можно эмулировать их, вводя третий объект, хранящий часть связей.

языки же, в которых GC учитывает циклические ссылки, собирают мусор (и, следовательно, вызывают деструкторы) недетерминированно.
в отличие от нашего любимого php.
 

atv

Новичок
наличие циклических ссылок - в контексте php - является следствием ошибок проектирования
А что значит "в контексте php"? Т.е. вне этого контекста это уже не является ошибкой проектирования?

даже тогда, они действительны нужны, можно эмулировать их, вводя третий объект, хранящий часть связей.
Код в студию...
 

Gorynych

Посетитель PHP-Клуба
м-да... после определенного количества постингов тема всегда переходит в оффтоп. Жаль. Тут было что обсудить.
 
Сверху