Zarządzanie pamięcią Obj-C
Zarządzanie pamięcią jest jednym z najważniejszych procesów w każdym języku programowania. Jest to proces, w którym pamięć obiektów jest przydzielana, gdy są wymagane, i zwalniana, gdy nie są już potrzebne.
Zarządzanie pamięcią obiektów to kwestia wydajności; jeśli aplikacja nie zwalnia niepotrzebnych obiektów, jej zużycie pamięci rośnie i spada wydajność.
Techniki zarządzania pamięcią typu Objective-C można ogólnie podzielić na dwa typy.
- „Manual Retain-Release” lub MRR
- „Automatyczne liczenie referencji” lub ARC
„Manual Retain-Release” lub MRR
W MRR jawnie zarządzamy pamięcią, samodzielnie śledząc obiekty. Jest to realizowane przy użyciu modelu zwanego zliczaniem odwołań, który zapewnia klasa Foundation NSObject w połączeniu ze środowiskiem wykonawczym.
Jedyna różnica między MRR i ARC polega na tym, że zatrzymanie i zwolnienie jest obsługiwane przez nas ręcznie w pierwszym przypadku, podczas gdy w drugim przypadku jest to obsługiwane automatycznie.
Poniższy rysunek przedstawia przykład działania zarządzania pamięcią w Objective-C.
Cykl życia pamięci obiektu klasy A pokazano na powyższym rysunku. Jak widać, licznik zachowań jest pokazany poniżej obiektu, gdy licznik zachowań obiektu osiąga wartość 0, obiekt jest całkowicie zwalniany, a jego pamięć jest zwalniana do użycia przez inne obiekty.
Klasa Obiekt jest najpierw tworzony przy użyciu metody przydzielania / inicjowania dostępnej w NSObject. Teraz liczba zachowań wynosi 1.
Teraz klasa B zachowuje obiekt klasy A, a liczba zachowań obiektu klasy A wynosi 2.
Następnie klasa C tworzy kopię obiektu. Teraz jest tworzony jako kolejna instancja klasy A z takimi samymi wartościami dla zmiennych instancji. Tutaj liczba zachowań wynosi 1, a nie liczba zachowań oryginalnego obiektu. Przedstawia to przerywana linia na rysunku.
Skopiowany obiekt jest zwalniany przez klasę C przy użyciu metody Release, a licznik przechowywania wynosi 0, a zatem obiekt jest niszczony.
W przypadku początkowego obiektu klasy A licznik zatrzymania wynosi 2 i należy go dwukrotnie zwolnić, aby został zniszczony. Odbywa się to poprzez oświadczenia o dopuszczeniu klasy A i klasy B, które zmniejszają liczbę zatrzymań odpowiednio do 1 i 0. Ostatecznie obiekt zostaje zniszczony.
Podstawowe zasady MRR
Jesteśmy właścicielami każdego utworzonego przez nas obiektu: tworzymy obiekt przy użyciu metody, której nazwa zaczyna się od „przydziel”, „nowy”, „kopiuj” lub „mutableCopy”
Możemy przejąć na własność obiekt używając retain: otrzymany obiekt ma zwykle gwarancję, że pozostanie ważny w metodzie, w której został odebrany, a metoda ta może również bezpiecznie zwrócić obiekt do jego wywołującego. Używamy retain w dwóch sytuacjach -
W implementacji metody akcesora lub metody init, aby przejąć na własność obiekt, który chcemy przechowywać jako wartość właściwości.
Aby zapobiec unieważnieniu obiektu jako efekt uboczny innej operacji.
Kiedy już go nie potrzebujemy, musimy zrzec się prawa własności do obiektu, który posiadamy: Zrzekamy się prawa własności do obiektu, wysyłając mu komunikat o zwolnieniu lub wiadomość o automatycznym zwolnieniu. W terminologii Cocoa zrzeczenie się własności obiektu jest zatem zwykle określane jako „zwolnienie” obiektu.
Nie możesz zrzec się prawa własności do obiektu, którego nie jesteś właścicielem: jest to po prostu konsekwencja wcześniejszych zasad polityki określonych wyraźnie.
#import <Foundation/Foundation.h>
@interface SampleClass:NSObject
- (void)sampleMethod;
@end
@implementation SampleClass
- (void)sampleMethod {
NSLog(@"Hello, World! \n");
}
- (void)dealloc {
NSLog(@"Object deallocated");
[super dealloc];
}
@end
int main() {
/* my first program in Objective-C */
SampleClass *sampleClass = [[SampleClass alloc]init];
[sampleClass sampleMethod];
NSLog(@"Retain Count after initial allocation: %d",
[sampleClass retainCount]);
[sampleClass retain];
NSLog(@"Retain Count after retain: %d", [sampleClass retainCount]);
[sampleClass release];
NSLog(@"Retain Count after release: %d", [sampleClass retainCount]);
[sampleClass release];
NSLog(@"SampleClass dealloc will be called before this");
// Should set the object to nil
sampleClass = nil;
return 0;
}
Kiedy kompilujemy powyższy program, otrzymamy następujące dane wyjściowe.
2013-09-28 04:39:52.310 demo[8385] Hello, World!
2013-09-28 04:39:52.311 demo[8385] Retain Count after initial allocation: 1
2013-09-28 04:39:52.311 demo[8385] Retain Count after retain: 2
2013-09-28 04:39:52.311 demo[8385] Retain Count after release: 1
2013-09-28 04:39:52.311 demo[8385] Object deallocated
2013-09-28 04:39:52.311 demo[8385] SampleClass dealloc will be called before this
„Automatyczne liczenie referencji” lub ARC
W automatycznym zliczaniu referencji lub ARC system używa tego samego systemu zliczania referencji, co MRR, ale wstawia wywołania odpowiedniej metody zarządzania pamięcią w czasie kompilacji. Gorąco zachęcamy do korzystania z ARC przy nowych projektach. Jeśli używamy ARC, zwykle nie ma potrzeby rozumienia podstawowej implementacji opisanej w tym dokumencie, chociaż w niektórych sytuacjach może to być pomocne. Aby uzyskać więcej informacji na temat ARC, zobacz Przejście do informacji o wersji ARC.
Jak wspomniano powyżej, w ARC nie musimy dodawać metod zwalniania i zachowywania, ponieważ zajmie się tym kompilator. W rzeczywistości podstawowy proces Celu C jest nadal taki sam. Wykorzystuje wewnętrznie operacje zachowywania i zwalniania, ułatwiając programistę kodowanie bez martwienia się o te operacje, co zmniejszy zarówno ilość napisanego kodu, jak i możliwość wycieków pamięci.
Istniała inna zasada zwana garbage collection, która jest używana w Mac OS-X wraz z MRR, ale od czasu jej wycofania w OS-X Mountain Lion nie była omawiana razem z MRR. Ponadto obiekty iOS nigdy nie miały funkcji czyszczenia pamięci. A w przypadku ARC nie ma również zastosowania czyszczenia pamięci w OS-X.
Oto prosty przykład ARC. Zauważ, że to nie zadziała na kompilatorze online, ponieważ nie obsługuje ARC.
#import <Foundation/Foundation.h>
@interface SampleClass:NSObject
- (void)sampleMethod;
@end
@implementation SampleClass
- (void)sampleMethod {
NSLog(@"Hello, World! \n");
}
- (void)dealloc {
NSLog(@"Object deallocated");
}
@end
int main() {
/* my first program in Objective-C */
@autoreleasepool {
SampleClass *sampleClass = [[SampleClass alloc]init];
[sampleClass sampleMethod];
sampleClass = nil;
}
return 0;
}
Kiedy kompilujemy powyższy program, otrzymamy następujące dane wyjściowe.
2013-09-28 04:45:47.310 demo[8385] Hello, World!
2013-09-28 04:45:47.311 demo[8385] Object deallocated