הפניות חלשות#

Perl עוקב אחר חיי עצמים עם ספירת הפניות. כל ערך יודע כמה הפניות מצביעות עליו; כשהמונה יורד לאפס, הערך משוחרר. זה עובד היטב לנתונים בצורת עץ, שבהם הפניות זורמות בכיוון אחד, מהורה לילד. זה נכשל ב־מחזורים, שבהם שרשרת של הפניות מצביעה בסופו של דבר חזרה למשהו מוקדם יותר בשרשרת. חברי המחזור משאירים זה את זה בחיים אפילו כשאף דבר מחוץ למחזור אינו מפנה אליהם עוד.

הפניה חלשה היא הפניה שאינה מגדילה את מונה ההפניות של הישות המוצבעת. היא מאפשרת לכם להצביע על משהו מבלי להשאיר אותו בחיים. הפניות חלשות הן התשובה הסטנדרטית של Perl למחזורי הפניות.

איך מחזור דולף#

שקלו צומת ברשימה דו־קשורה המחזיק גם הפניות next וגם prev:

sub new_node {
    my ($value) = @_;
    return { value => $value, next => undef, prev => undef };
}

my $a = new_node('A');
my $b = new_node('B');
$a->{next} = $b;
$b->{prev} = $a;

ל־$a ול־$b יש כל אחד מונה הפניות של 2:

  • $a מופנה על־ידי הלקסיקלי $a וגם על־ידי $b->{prev}.

  • $b מופנה על־ידי הלקסיקלי $b וגם על־ידי $a->{next}.

כאשר הלקסיקלים יוצאים מהתחום, התרומה שלהם יורדת:

  • $a יורד למונה 1 (רק $b->{prev} נשאר).

  • $b יורד למונה 1 (רק $a->{next} נשאר).

אף אחד מהם אינו מגיע לאפס. שני הצמתים אינם נגישים משום מקום בתוכנית שלכם, אך הם משאירים זה את זה בחיים לאורך חיי המפרש. זוהי דליפה.

תיקון הדליפה עם weaken#

Scalar::Util::weaken הופך הפניה קיימת לחלשה. ההפניה עדיין עובדת ל־dereference; היא פשוט אינה נספרת לשמירת המטרה בחיים.

התבנית הסטנדרטית: בכל כיוון שאתם רואים כ״מצביע אחורה״ או ״מצביע הורה״, הפכו את זה לחלש.

use Scalar::Util qw(weaken);

my $a = new_node('A');
my $b = new_node('B');
$a->{next} = $b;
$b->{prev} = $a;
weaken $b->{prev};             # the back-pointer

עכשיו מונה ההפניות של $a נשאר ב־1 (רק הלקסיקלי $a תורם; $b->{prev} חלש ואינו נספר). כאשר $a יוצא מהתחום, $a משוחרר - ומכיוון ששחרור $a מוריד את $b->{next}, $b משוחרר גם הוא. המבנה השלם מתמוטט.

מתי הפניה חלשה הופכת ל־undef#

אם הישות המוצבעת משוחררת בעוד הפניה חלשה עדיין קיימת, ההפניה החלשה הופכת אוטומטית ל־undef. אתם לא רואים אף פעם מצביע תלוי:

use Scalar::Util qw(weaken);

my $target = { name => 'alive' };
my $weak   = $target;
weaken $weak;

print defined $weak ? "yes\n" : "no\n";   # yes
undef $target;                             # last strong ref gone
print defined $weak ? "yes\n" : "no\n";   # no

defined $weak היא הדרך הסטנדרטית לבדוק אם המטרה עדיין שם. ראו defined.

היכן הפניות חלשות שייכות#

הכלל: השתמשו בהפניה חלשה עבור המצביע ״שאינו בעלים״ בכל זוג הפניות שאחרת היה יוצר מחזור.

  • רשימה דו־קשורה. next בעלים; prev חלש.

  • עץ הורה/ילד עם מצביעי הורה. children בעלים; parent בכל ילד חלש.

  • תבנית Observer. הסובייקט מחזיק הפניות חלשות ל־observers שלו; observers מחזיקים הפניות חזקות לסובייקט. כאשר observer מוריד את ההפניה שלו, הוא נעלם; הרשומה של הסובייקט עבורו הופכת ל־undef וניתנת לטאטוא.

  • Caches שאסור שישאירו רשומות בחיים. cache שערכיו הם הפניות חלשות אינו מאריך את חיי העצמים המאוחסנים.

isweak - בדיקת הפניה#

Scalar::Util::isweak אומר לכם האם סקלר נתון מחזיק הפניה חלשה:

use Scalar::Util qw(weaken isweak);

my $x = { k => 1 };
my $y = $x;
print isweak($y) ? "weak\n" : "strong\n";   # strong
weaken $y;
print isweak($y) ? "weak\n" : "strong\n";   # weak

שימושי בעיקר כשמדבגים בעיית חיים. קוד production לעיתים רחוקות מסתעף על isweak.

טעויות נפוצות#

  • החלשה של הצד הלא נכון. החלשה של המצביע ״קדימה״ (זה שבעל יורש) משחרר את היורש מיד, לפני שאתם משתמשים בו אי פעם. חלש הולך על המצביע אחורה.

  • החלשת לקסיקלי. weaken $ref כאשר $ref הוא לקסיקלי רענן המפנה ל־$target הופך את הלקסיקלי לחלש. אם דבר אחר אינו מחזיק הפניה חזקה ל־$target, הוא נעלם בהוראה הבאה ו־$ref הופך ל־undef. הפניות חלשות שייכות לחריצי מבני־נתונים, בדרך כלל לא ללקסיקלים מקומיים.

  • הנחה שהפניות חלשות הן אוטומטיות. Perl אינו מזהה מחזורים ומחליש אותם בשבילכם. אתם חייבים להצטרף.

מעבר ל־Scalar::Util#

Scalar::Util נשלח עם Perl; הוא תמיד זמין. לצרכים מורכבים יותר - WeakRef, Hash::Util::FieldHash - פנו ל־CPAN. לקוד רגיל, weaken הוא כל מה שאתם צריכים.

לאן ממשיכים מכאן#

סיימתם את הדרכת ההפניות. קריאות הבאות טובות:

  • דף העיון של ref - הרשימה המלאה של המחרוזות שהוא מחזיר, כולל מקרי עצם־blessed.

  • bless - איך הפניית האש רגילה הופכת לעצם של מחלקה ספציפית.

  • הדרכת התכנות מונחה־העצמים, ברגע שאתם מוכנים למחלקות הבנויות על־גבי ההפניות שאתם מבינים עכשיו.