Pomoc - Szukaj - Użytkownicy - Kalendarz
Pełna wersja: agregacja a usuwanie obiektu
Forum PHP.pl > Forum > PHP > Object-oriented programming
dtb
mam przykładowo taki kod:
  1. <?php
  2.  
  3. class A {
  4. private $foo;
  5. public function assign($obj) {
  6. $this->foo = $obj;
  7. }
  8. public function __destruct() {
  9. echo "die A\n";
  10. }
  11. }
  12. class B {
  13. public function __destruct() {
  14. echo "die B\n";
  15. }
  16. }
  17.  
  18. $a = new A;
  19. $b = new B;
  20. $a->assign($b);
  21. unset($b);
  22. echo "---\n";
  23.  
  24. ?>

spodziewałem się takiego efektu:
Cytat
die B
---
die A

czyli unset niszczy $b, następnie wyświetlane są "---" i wraz z końcem skryptu usuwany jest obiekt $a.
tymczasem wynik jest taki:
Cytat
---
die A
die B

unset w ogóle nie działa.
może mi ktoś powiedzieć jak usunąć obiekt $b? z góry dziękuje za odpowiedź.

EDIT: $foo musi być prywatna
-=Peter=-
Unset usuwa tylko referencję do do zmiennej, a nie samą zmienną. Pamięć jest zwalniana gdy na daną zmienną nie wskazuje już żadna referencja (np. gdy usunięta zostanie ostatnia referencja). W Twoim kodzie istnieje jeszcze referencja do obiektu $b w obiekcie $a i dlatego obiekt $b nie został usunięty (nie wywołał się destruktor).
Pr0100
  1. <?php
  2.  
  3. class A {
  4. private $foo;
  5. public function assign($obj) {
  6. $this->foo &= $obj;
  7. }
  8. public function __destruct() {
  9. echo "die A\n";
  10. }
  11. }
  12. class B {
  13. public function __destruct() {
  14. echo "die B\n";
  15. }
  16. }
  17.  
  18. $a = new A;
  19. $b = new B;
  20. $a->assign($b);
  21. unset($b);
  22. echo "---\n";
  23.  
  24. ?>


efekt
Kod
die B
---
die A


  1. <?php
  2.  
  3. class A {
  4. private $foo;
  5. public function assign($obj) {
  6. $this->foo = clone $obj;
  7. }
  8. public function __destruct() {
  9. echo "die A\n";
  10. }
  11. }
  12. class B {
  13. public function __destruct() {
  14. echo "die B\n";
  15. }
  16. }
  17.  
  18. $a = new A;
  19. $b = new B;
  20. $a->assign($b);
  21. unset($b);
  22. echo "---\n";
  23.  
  24. ?>


efekt
Kod
die B
---
die A
die B
LBO
-=Peter=- wyjaśnił dlaczego dzieje się tak, a nie inaczej.
Pr0100 wyjaśnił jak można to obejść.

Ja od siebie dodam kod, który to zobrazuje istotę problemu.
  1. class A {
  2. private $b;
  3.  
  4. public function setB(B $b) {
  5. $this->b = $b;
  6. }
  7.  
  8. public function unsetB() {
  9. unset($this->b);
  10. }
  11.  
  12. public function __destruct() {
  13. echo __METHOD__ . PHP_EOL;
  14. }
  15. }
  16.  
  17. class B {
  18. public function __destruct() {
  19. echo __METHOD__ . PHP_EOL;
  20. }
  21. }
  22.  
  23. $a = new A;
  24. $b = new B;
  25. $a->setB($b);
  26. $a->unsetB(); // usuwasz $b w obiekcie $a, zostało jeszcze "globalne" $b (użyłem ", bo to nie jest oczywiście prawdziwie globalna zmienna).
  27.  
  28. unset($b); // usuwasz "globalne" $b , nie ma już nigdzie $b . Destruktor B się wykona.
  29. // w przykładzie Pr0100 istnieje tylko "globalne" $b
  30.  
  31. echo "---" . PHP_EOL;


Wynik:
Cytat
B::__destruct
---
A::__destruct


Zostaje nadal problem samych destruktorów i czy warto ich używać. Kilka miesięcy temu, ktoś poruszył podobny temat i tutaj masz link do tych kilku zdań, które wtedy napisałem.
dtb
klonowanie, ani rozwiązanie LBO absolutnie nie wchodzi w grę.
LBO: destruktorów tu użyłem jedynie do debugowania i aby zobrazować problem.
dzięki pr0100: próbowałem wcześniej z =&, a miało być &=.
zegarek84
Cytat(dtb @ 16.02.2010, 19:01:12 ) *
dzięki pr0100: próbowałem wcześniej z =&, a miało być &=.

muszę Cię jednak rozczarować winksmiley.jpg - włącz sobie raportowanie błędów winksmiley.jpg
$a &= $b
oznacza
$a = $a & $b

unset usuwa tutaj tylko referencję - obiekt jest niczszony po zniszczeniu ostatniej możliwej referencji... i tu swoją drogą logikę jaką chcesz uzyskać jest dla mnie troszku nie zrozumiała - skoro obiekt A potrzebuje obiektu B to obiekt B powinien zostać zniszczony na końcu tak jak się to dzieje - a w php 5 każdy obiekt domyślnie jest przekazywany przez referencję... jest sposób na obejście tego ale do sklepu się śpieszę - jak wrócę pomyślę winksmiley.jpg (no clon - czyli inny obiekt Cię nie interesuje ^^)
dtb
nigdzie nie powiedziałem, że obiekt A potrzebuje B. wręcz przeciwnie. w moim projekcie A (nazwijmy go WINDOW) i B (BUTTON) są widgetami. BUTTON samodzielnie nie może być wyświetlony, potrzebuje do tego WINDOW, który wyświetli widgety na nim. chodzi o to, by można było np. usunąć BUTTON - wtedy destruktor wyśle informacje do klienta (ajax), aby usunąć widget. rozwiązanie LBO jest nieintuicyjne i niewygodne w przypadku bardziej rozbudowanej hierarchii, jak np. WINDOW > TABLE > TOOLBAR > BUTTON.
warto wspomnieć, że nazwa zmiennej, jest jednocześnie unikalnym id widgetu przez który się odwołuje do niego. czyli nie:
$window1->table1->toolbar1->button1, tylko samo $button1.

zdziwiło mnie rozwiązanie z &=, ale działa tak jak oczekiwałem.
LBO
Już drugi raz wyskakuje mój nick, dlatego, czy mogę się zapytać Ciebie @dtb, co dokładnie uważasz za moje rozwiązanie?

1. Mój poglądowy kod? Bo to nie miało służyć jako żadne rozwiązanie, tylko wyjaśnić problem. Cytuję: "Ja od siebie dodam kod, który to zobrazuje istotę problemu.".
Dam coś prostszego:

  1. class A {
  2. public function __destruct() { echo __METHOD__ . PHP_EOL;}
  3. }
  4.  
  5. $a1 = new A;
  6. $a2 = $a1;
  7. unset($a1); // destruktor się nie wykona, bo istnieje jeszcze $a2
  8. print '---' . PHP_EOL;


2. Koncepcję z linku który podałem? Bo w życiu nie nazwałbym go nieintuicyjnym, ani niewygodnym. Szczerze? Nie wyobrażam sobie, abyś nie użył analogicznego rozwiązania do Twojego WINDOW > TABLE > TOOLBAR > BUTTON.
dtb
ech... niepotrzebnie zaczynałem dyskusję.

przyczepiłem się tego, bo myślałem, że to proponowane przez Ciebie rozwiązanie (mój błąd - źle przeczytałem):
Kod
$a->unsetB();
unset($b);

takie rozwiązanie oczywiście nie pasuje, bo załóżmy że mam więcej obiektów wrzuconych do $a:
Kod
$a->assign($button1 = new B);
$a->assign($button2 = new B);
$a->assign($button3 = new B);
$a->assign($foo = new B);

ich liczba nie jest określona. jak bym chciał usunąć obiekt $button3, to nie mogę przecież utworzyć metody unsetButton3 (i tak dla każdej potencjalnej nazwy zmiennej). to było by bez sensu.

natomiast zaproponowany przez ciebie system zarządzania obiektami nie jest rozwiązaniem jakiego oczekuje, ponieważ nie chodzi mi o kolejność wykonywania destruktorów, a jedynie o możliwość usunięcia pojedynczej instancji wraz z wszystkimi jej referencjami w dowolnym miejscu w kodzie. widać źle zrozumiałeś moje intencje na samym początku.
LBO
Cytat(dtb @ 22.02.2010, 01:02:43 ) *
[...] ich liczba nie jest określona. jak bym chciał usunąć obiekt $button3, to nie mogę przecież utworzyć metody unsetButton3 (i tak dla każdej potencjalnej nazwy zmiennej). to było by bez sensu. [...]

Masz kilka opcji:

1. Bawisz się w klonowanie. Wtedy zużycie pamięci może skoczyć pod sufit, a to może boleć.

2. &= naprawdę nie działa. Zdebuguj to sobie dokładnie.

3. Bawisz się rozwiązaniem z linku. Nie ma ono za zadanie tylko zastępować destruktorów, ono ma zarządzać obiektami. Wymaga to więcej pracy i planowania, ale jest do zrobienia.
  1. $a->assign($button1 = new B);

Nie powinno być:
  1. $a->assign('button1', new B);

Cytat(dtb @ 22.02.2010, 01:02:43 ) *
[...] to było by bez sensu.


Nie, jak będziesz miał coś w stylu:
  1. $a->remove('button1');


Wszystko rozbija się o abstrakcję.
To jest wersja lo-fi głównej zawartości. Aby zobaczyć pełną wersję z większą zawartością, obrazkami i formatowaniem proszę kliknij tutaj.
Invision Power Board © 2001-2025 Invision Power Services, Inc.