פעם שעברה ראינו כיצד שימוש לא זהיר בEventים יכול לגרום לדליפת זיכרון.
ראינו גם שאפשר למנוע את הדליפה ע”י קריאה לפונקציה שמבטלת את ההרשמה לאירועים רגע לפני שהאובייקט עוזב.
בסדרת הפוסטים הקרובה נראה כיצד אפשר לפתור את הבעיה בצורה אחרת…
השיטה היא באמצעות שימוש בטיפוס שנקרא WeakReference.
מה זה בדיוק WeakReference?
WeakReference היא טיפוס המצביע לReference כלשהו, אלא שבניגוד לשימוש רגיל בobject, מופע של WeakReference אינו נספר בתור Reference לאובייקט מבחינת הGarbage Collector. לכן, אם לאף אחד חוץ מלWeakReference אין Reference לאובייקט מסוים, האובייקט ייאסף ע”י הGarbage Collector.
נשמע טוב, לא?
אז מה הקשר לEventים?
אמרנו שמה שגורם לדליפת זכרון הוא שכל Delegate מחזיק Reference לאובייקט אליו שייכת המתודה. לכן, מבחינת ספירה, הGarbage Collector לעולם לא יאסוף את האובייקט שהמתודה שייכת אליו, שהרי עדיין לDelegate יש Reference אליו.
מה שאנחנו יכולים לעשות זה ליצור מעין EventHandler משלנו בו הTarget יהיה WeakReference ואז איכשהו להפעיל את המתודה שלו.
ככה הReference לאובייקט לא יספר בספירה של הGarbage Collector, ולכן יאסף.
אז נתחיל:
דבר ראשון, אנחנו מעוניינים שהטיפוס שלנו יחקה את הDelegateששמו EventHandler
|
|
זאת כבר התחלה טובה, כי קוד כזה יתקמפל:
|
|
למרבה הצער, אנחנו לא יכולים לרשת מ"הטיפוסים המיוחדים" ששמם Delegate, MulticastDelegate וEventHandler<TEventArgs>, או מכל Delegate אחר.
לכן נצטרך למצוא איזשהו Workaround, שהרי היינו רוצים שהשורה הבאה תתקמפל:
|
|
הפתרון, למקרה שלא ניחשתם הוא להשתמש בimplict cast:
|
|
כמובן, צריך לשים פה מימוש מתאים, שהרי ככה זה לא יעבוד 😃
בואו נדבר על המימוש:
מה שאנחנו בעצם מעוניינים לעשות זה דבר כזה:
כשמפעילים את הEvent שלנו אנחנו מעוניינים לבדוק האם הReference של האובייקט עדיין קיים, ובמידה וכן, להפעיל את הפונקציה שלו.
מה שנעשה זה ניצור פונקציה אליה יכנסו בכל קריאה לWeak-event שלנו. בה אנחנו נבצע את הבדיקה וכו’:
|
|
ואז בהסבה לEventHandler<TEventArgs>ניצור פשוט Delegate לפונקציה זו:
|
|
עכשיו מה שיקרה זה שכל פעם שיקפיצו את הWeak-event שלנו, תקרא הפונקציה Invoke. אם למשל נכתוב בה קוד כזה:
|
|
אז אם נרשם לאיזשהו Event ככה:
|
|
אז כשיוקפץ הEvent הנ"ל, נכנס לפונקציה Invoke ולכן יכתבו למסך התאריך והשעה.
בהמשך נראה מה בדיוק נכתוב בפונקציה Invoke ואיך זה מתקשר לאותם WeakReferenceים.
המשך יום מלא אירועים לא חלשים טוב