Zarządzanie pamięcią

Pierwsze pytanie większości programistów Java dotyczy sposobu wdrożenia zarządzania pamięcią przez J2ObjC, ponieważ w języku Java działa mechanizm odśmiecania pamięci, a klasy Objective-C domyślnie nie robią tego. iOS udostępnia 2 metody zarządzania pamięcią: liczenie plików referencyjnych i automatyczne liczenie plików referencyjnych (ARC).

J2ObjC generuje różne kody zarządzania pamięcią w zależności od wybranej metody. Przetłumacz za pomocą opcji -use-arc, aby wygenerować kod korzystający z ARC. Domyślnie używane jest ręczne zliczanie plików referencyjnych.

Liczenie referencyjne

Metoda zliczania plików referencyjnych wyraźnie określa prawo własności do obiektu. Metoda jest właścicielem obiektu w momencie jego utworzenia, dopóki go nie zwolni. Po otrzymaniu obiektu z innej metody metoda odbierająca zachowuje go, jeśli trzeba się do niego odwołać po zwróceniu metody. Gdy metoda nie musi się już odwoływać do obiektu, musi ją zwolnić. Gdy liczba zachowywania obiektu wynosi 0, jego pamięć jest zwalniana, a obiekt przestaje być prawidłowy. Przy uwolnieniu obiektów wywoływana jest ich metoda offerloc() w celu zwolnienia własności zmiennych instancji.

Jednym z problemów z tą metodą jest przeniesienie własności obiektu. Na przykład w celu utworzenia obiektu metoda fabryczna może zostać wywołana. Jeśli metoda fabryczna uwolni obiekt, zanim go zwróci (ponieważ nie chce już być właścicielem obiektu), zostanie on uwolniony, zanim metoda wywołania będzie mogła go zachować.

Aby przenieść własność obiektu, metoda wysyła do niego wiadomość o automatycznym wydaniu (zamiast komunikatu o wersji), co opóźnia komunikat o wersji. Dzięki temu metoda fabryczna może utworzyć obiekt, który ma zostać zwrócony, i zrzekać się jego własności bez unieważnienia obiektu. W regularnych odstępach czasu (na przykład po każdym powtórzeniu pętli zdarzeń w aplikacji na iOS) pula automatycznie zwalniana jest „opróżniana”, co oznacza, że wszystkie obiekty w tej puli są wysyłane komunikaty o odroczonej wersji. Wszystkie obiekty, których liczba utrzymania spadnie do 0, są zwalniane jak zwykle.

Za zarządzanie pamięcią odpowiada deweloper, więc łatwo można wyciek pamięci za pomocą metody zliczania plików referencyjnych. Firma Apple zaleca jednak kilka sprawdzonych metod, które pozwalają zminimalizować ten problem, wdrażane przez J2ObjC.

Dostępne jest też środowisko wykonawcze i narzędzia do wykrywania wycieków pamięci. Środowisko wykonawcze Objective-C zgłasza wszelkie wykryte wycieki przy zamykaniu aplikacji, co jest jedną z przyczyn, dla których J2ObjC przekształca testy JUnit w wykonywalne pliki binarne. Xcode wykorzystuje interfejs Clang, a kompilator przeprowadza doskonałą analizę statyczną pod kątem problemów z pamięcią, którą Xcode udostępnia za pomocą polecenia Analyze.

Automatyczne zliczanie plików referencyjnych (ARC)

ARC to zalecana przez Apple metoda zarządzania pamięcią. Przenosi odpowiedzialność za zliczanie odwołań do kompilatora, który dodaje podczas kompilacji odpowiednie metody przechowywania, wydania i automatycznego zwalniania. ARC obsługuje słabe odniesienia na urządzeniach z iOS 5 i nowszym.

Zalecamy, aby projekty korzystały z ARC do tłumaczenia kodu. Transpilowany kod Objective-C jest taki sam jak odręczny kod Objective-C. Korzystanie z ARC może pomóc deweloperom uniknąć typowych błędów związanych z pamięcią, takich jak zbyt duże zwolnienie pamięci lub niedostateczne odniesienie do nich, co upraszcza zarządzanie pamięcią i zmniejsza ryzyko wystąpienia błędów.

Pamiętaj, że domyślnie ARC nie jest wyjątkiem. W szczególności powoduje to wyciek pamięci przy zgłaszanych wyjątkach. Ponieważ wyjątki występują częściej w języku Java i zwykle można je odzyskać, może to być problematyczne. Użycie parametru -fobjc-arc-exceptions w przypadku kompilacji za pomocą łuku niweluje wycieki i utrzymuje akceptowany przez nas koszt wydajności.

Zalecamy też, aby nowe projekty wykorzystywały kod ARC w odręcznym kodzie Objective-C i korzystali z ręcznego liczenia plików referencyjnych tylko wtedy, gdy profilowanie wykazuje rzeczywisty problem z wydajnością. Zarówno kod ARC, jak i kod inny niż ARC można bez problemu skompilować i połączyć z tą samą aplikacją.

Słabe odwołania

Do pól można dodawać adnotacje za pomocą metody com.google.devtools.j2objc.Weak, której transpiler wykorzystuje do generowania pól zgodnych ze słabą semantyką odwołania w standardzie Objective-C. Podczas zliczania odwołań oznacza to, że pole nie jest zachowywane po zainicjowaniu i jest automatycznie zwalniane po wydaniu instancji, która zawiera dane. W ARC słabe pola są oznaczone adnotacją __unsafe_unretained, a powiązane z nimi właściwości są dezaktywowane.

W niektórych przypadkach instancja klasy wewnętrznej trafia w cykl odwołania ze swoją instancją zewnętrzną. W tym przypadku do oznaczenia klasy wewnętrznej używana jest adnotacja com.google.devtools.j2objc.WeakOuter, więc odniesienie do klasy zewnętrznej jest traktowane w sposób opisany powyżej. Adnotacja nie ma wpływu na inne pola w klasie wewnętrznej.

J2ObjC obsługuje też klasę WeakReference, więc korzystający z niej kod w Javie będzie działać tak samo po translacji. Pamiętaj, że właściwość WeakReference z natury nie jest deterministyczna w przypadku JVM. Aplikacje, które chcą uniknąć wycieków pamięci, zachowując przewidywalność, powinny preferować @Weak i @WeakOuter.

Narzędzia do zarządzania pamięcią