Jeśli mam fragment kodu, który wygląda tak:

- (void)testSomething
{
  __weak NSString *str = [[NSString alloc] initWithFormat:@"%@", [NSDate date]];
  NSLog(@"%@", str);
}

Wynik będzie miał wartość (null), ponieważ nie ma silnych odwołań do str i zostanie natychmiast zwolniony po jego przydzieleniu. Ma to sens i jest opisane w przewodniku Przejście do ARC.

Jeśli mój kod wygląda tak:

- (void)testSomething
{
  __weak NSString *str = [NSString stringWithFormat:@"%@", [NSDate date]];
  NSLog(@"%@", str);
}

Następnie poprawnie drukuje aktualną datę. Oczywiście można by się spodziewać, że będzie działać w świecie innym niż ARC, ponieważ str zostanie wydane automatycznie, a zatem będzie ważne do zakończenia tej metody. Jednak w kodzie obsługującym ARC ludzie zazwyczaj uważają te dwie formy (stringWithFormat i alloc/initWithFormat) za równoważne.

Więc moje pytanie brzmi, czy kod taki jak w drugim przykładzie gwarantuje działanie pod ARC. Oznacza to, że jeśli mam słabe odniesienie do obiektu, który otrzymuję za pośrednictwem tego, co normalnie uznalibyśmy za wygodny konstruktor automatycznego zwalniania, czy gwarantowane jest bezpieczne użycie tego odniesienia w tym samym zakresie, w jakim normalnie bym mieć bez ARC (tzn. do momentu wyjścia metody)?

5
UIAdam 9 luty 2012, 02:54

2 odpowiedzi

Najlepsza odpowiedź

Konwencje automatycznego zwalniania i przydzielania nadal obowiązują w świecie ARC. Jedyną różnicą jest to, że ARC wstawi dodatkowe wywołania zatrzymania/zwolnienia, aby znacznie utrudnić wyciek obiektów lub dostęp do cofniętego obiektu.

W tym kodzie:

__weak NSString *str = [[NSString alloc] initWithFormat:@"%@", [NSDate date]];

Jedynym miejscem, w którym obiekt jest zachowywany (lub jego odpowiednikiem), jest alokacja. ARC automatycznie wstawi polecenie zwolnienia, powodując jego natychmiastowe zwolnienie.

Tymczasem w tym kodzie:

 __weak NSString *str = [NSString stringWithFormat:@"%@", [NSDate date]];

Zgodnie z konwencją, wartość zwracana przez wygodny konstruktor, taki jak ten, musi być automatycznie zwolnionym obiektem*. Oznacza to, że bieżąca pula autorelease zachowała obiekt i nie zwolni go, dopóki pula nie zostanie opróżniona. Masz zatem prawie gwarancję, że ten obiekt będzie istniał przynajmniej przez czas trwania twojej metody - chociaż prawdopodobnie nie powinieneś polegać na tym zachowaniu.

(* lub zachowane w inny sposób)

5
grahamparks 17 listopad 2012, 23:48

Czas życia lokalnej słabej zmiennej nie jest w ogóle gwarantowany. Jeśli obiekt, na który wskazuje zmienna, jest cofnięty, słaba zmienna wskaże później nil.

Jeśli masz słabe odniesienie do obiektu, który uzyskałeś za pomocą metody, która nie zwraca zachowanego obiektu, nie jest bezpieczne założenie, że ten obiekt żyje do momentu zakończenia działania metody. Jeśli chcesz mieć pewność, że obiekt przetrwa, użyj silnego odniesienia.

Oto przykład, który pokazuje, że nie ma gwarancji, że wartość zwracana przez niezachowaną metodę trafi do puli automatycznego zwalniania:

  • Utwórz nowy projekt iOS (aplikacja Single View przy użyciu ARC i Storyboards)
  • Dodaj tę metodę do AppDelegate.m:

    + (id)anObject
    {
        return [[NSObject alloc] init];
    }
    
  • Zastąp -application:didFinishLaunchingWithOptions::

    - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
    {
        __weak id x = [AppDelegate anObject];
        NSLog(@"%@", x);
        return YES;
    }
    
  • Ważne: teraz ustaw poziom optymalizacji debugowania na -Os.

W tym przykładzie +[AppDelegate anObject] działa jak wygodny konstruktor, ale zobaczysz zarejestrowane (null), jeśli uruchomisz go na urządzeniu z optymalizacją -Os. Powodem tego jest sprytna optymalizacja ARC, która zapobiega narzutom związanym z dodaniem obiektu do puli autorelease.

Być może zauważyłeś, że przełączyłem się na nieużywanie metody bibliotecznej, takiej jak +[NSString stringWithFormat:]. Wydaje się, że zawsze umieszczają one obiekty w puli autorelease, co może być spowodowane zgodnością.

1
Tammo Freese 18 listopad 2012, 03:43