ניהול הזיכרון

השאלה הראשונה של רוב מפתחי Java היא איך J2ObjC מטמיע את ניהול הזיכרון, כי ב-Java יש איסוף אשפה ו-Objective-C לא אפשרי כברירת מחדל. ב-iOS יש שתי שיטות לניהול זיכרון: ספירת הפניות וספירת הפניות אוטומטית (ARC).

J2ObjC יוצר קוד שונה לניהול הזיכרון, בהתאם לשיטה שנבחרה. יש לתרגם באמצעות האפשרות -use-arc כדי ליצור קוד שמשתמש ב-ARC. כברירת מחדל, המערכת משתמשת בספירה ידנית של קובצי עזר.

ספירת אסמכתאות

השיטה לספירת קובצי עזר מגדירה את הבעלות על אובייקטים באופן מפורש. שיטה היא הבעלים של אובייקט כשהיא יוצרת אותו, עד שהיא משחררת אותו. כשמקבלים אובייקט משיטה אחרת, השיטה המקבלת שומרת את האובייקט אם צריך להפנות אליו אחרי שה-method חוזר. כשהשיטה כבר לא צריכה להפנות לאובייקט, היא צריכה לשחרר אותו. כאשר ספירת השימור של אובייקט היא אפס, הזיכרון שלו משוחרר והאובייקט לא תקף יותר. כשמשחררים אובייקטים, ה-method של Dealloc() נקרא שחרור הבעלות על משתני המכונה שלהם.

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

כדי להעביר בעלות על אובייקט, השיטה שולחת לו הודעת שחרור אוטומטי (במקום הודעת אישור), שדוחה את ההודעה. כך אפשר ליצור אובייקט שיוחזר כאובייקט שיוחזר, ולוותר על הבעלות שלו בלי לבטל את התוקף של האובייקט. במרווחי זמן קבועים (למשל, אחרי כל איטרציה של לולאת אירוע באפליקציה ל-iOS), מאגר ההפצה האוטומטית "מרוסק". כלומר, כל האובייקטים במאגר הזה נשלחים להודעות שנדחו. אובייקטים שמספרי השמירה שלהם יורדים לאפס משוחררים כרגיל.

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

יש גם תמיכה בזמן ריצה ובכלים לזיהוי דליפות זיכרון. זמן הריצה של Objective-C מדווח על דליפות שזוהו בזמן יציאה מהאפליקציה, וזו אחת הסיבות לכך ש-J2ObjC מתרגם בדיקות JUnit לבינאריים להפעלה. ב-Xcode נעשה שימוש ב-Clang, ולמהדר הזה יש ניתוח סטטי מעולה לבעיות זיכרון, ש-Xcode הופך לזמין עם פקודת ה-Analysis.

ספירת הפניות אוטומטית (ARC)

ARC היא השיטה המומלצת של Apple לניהול זיכרון. היא מעבירה את האחריות על ספירת ההפניות להדר, ומוסיפה את השיטות המתאימות לשמירה, לשחרור ולשחרור אוטומטי במהלך ההידור. פרוטוקול ARC תומך בהפניות חלשות במכשירים עם iOS 5 ואילך.

מומלץ להשתמש ב-ARC לקוד מתורגם בפרויקטים. משתנה קוד אובייקט-C הוא בדיוק כמו קוד Objective-C בכתב יד. השימוש ב-ARC יכול לעזור למפתחים להימנע משגיאות נפוצות שקשורות לזיכרון, כמו שחרור יתר או חוסר התייחסות, וכך ניהול הזיכרון יהיה פשוט יותר ויהיה פחות סיכוי לשגיאות.

שימו לב שכברירת מחדל, ARC לא בטוח לחריגות. באופן ספציפי, כשמפעילים חריגות, הזיכרון דולף. החריגים הם נפוצים יותר ב-Java ובדרך כלל אפשר לשחזר אותם, ולכן הדבר עלול להיות בעייתי. שימוש ב--fobjc-arc-exceptions כשמבצעים הידור עם קשת יתקן את הדליפות עם עלות ביצועים סבירה מסוימת.

אנחנו גם ממליצים להשתמש ב-ARC עבור קוד Objective-C בכתב יד, ולהשתמש בספירה ידנית של הפניות רק אם בנתוני הפרופיל יש בעיית ביצועים אמיתית. אפשר להדר ולקשר קוד ARC וגם קוד שאינו ARC לאותה אפליקציה בלי בעיה.

הפניות חלשות

אפשר להוסיף הערות לשדות באמצעות הפקודה com.google.devtools.j2objc.Weak, שבה משתמש הטרנספילר כדי ליצור שדות בהתאם לסמנטיקה של ההפניות החלשות של Objective-C. כשמשתמשים בספירת הפניות, המשמעות היא שהשדה לא נשמר כשהוא מופעל, והוא משוחרר באופן אוטומטי אחרי שהמכונה שמכילה משוחררת. ב-ARC, שדות חלשים מסומנים בהערה __unsafe_unretained, והמאפיינים הקשורים אליהם מוצהרים כחלשים.

במקרים מסוימים, מכונה מסוג מחלקה פנימית נכנסת למחזור הפניה עם המכונה החיצונית שלה. כאן, הערה של com.google.devtools.j2objc.WeakOuter משמשת לסימון המחלקה הפנימית, כך שניתן להתייחס להפניה למחלקה החיצונית כפי שמתואר למעלה. שדות אחרים במחלקה הפנימית לא מושפעים מהערה זו.

J2ObjC תומך גם במחלקה WeakReference, ולכן קוד Java שמשתמש בה יפעל באותו אופן בתרגום. חשוב לזכור ש-WeakReference מטבעו לא מוגדר ב-JVM. אפליקציות שרוצים למנוע דליפות זיכרון תוך שמירה על יכולת החיזוי צריכות להעדיף את @Weak ואת @WeakOuter.

כלים לניהול הזיכרון

  • Cycle Finder Tool – מנתח קובצי מקור ב-Java למחזורים חזקים של הפניות לאובייקטים.
  • Xcode Instruments – חבילת הכלים ליצירת פרופילים של Xcode.
  • אבחון של זיכרון Xcode – מאפשר ליצור אפשרויות להפעלה עם אבחון ורישום של הזיכרון.