2013-09-26 10 views
38

Mam pytanie dotyczące silnych i słabych odniesień do siebie w blokach w iOS. Wiem, że to właściwy sposób odnieść się do siebie wewnątrz bloku jest stworzenie słabego odniesienia na zewnątrz bloku, a następnie silne odniesienie do tego słabego odniesienia wewnątrz bloku, jak to:Bloki iOS i silne/słabe odniesienia do siebie

__weak typeof(self) weakSelf = self; 
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0),^{ 
    typeof(self) strongSelf = weakSelf; 
    NSLog(@"%@", strongSelf.someProperty); 
}); 

Jednak to, co się dzieje, jeśli masz zagnieżdżone bloki? Czy wystarczy jeden zestaw referencji? A może potrzebujesz nowego zestawu dla każdego bloku? Na przykład, który z poniższych jest poprawny?

to:

__weak typeof(self) weakSelf = self; 
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0),^{ 
    typeof(self) strongSelf = weakSelf; 
    NSLog(@"%@", strongSelf.someProperty); 
    dispatch_async(dispatch_get_main_queue(),^{ 
     strongSelf.view.frame = CGRectZero; 
    }); 
}); 

Albo to:

__weak typeof(self) weakSelf = self; 
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0),^{ 
     typeof(self) strongSelf = weakSelf; 
     NSLog(@"%@", strongSelf.someProperty); 
     __weak typeof(strongSelf) weakSelf1 = strongSelf; 
     dispatch_async(dispatch_get_main_queue(),^{ 
      typeof(strongSelf) strongSelf1 = weakSelf1; 
      strongSelf1.view.frame = CGRectZero; 
     }); 
    }); 

Wszelkie informacje lub wyjaśnienie jest bardzo doceniane!

+0

dlaczego powinniśmy tworzyć silne odniesienie do słabych wewnątrz bloku? – BergP

Odpowiedz

52

Nie trzeba wykonywać dwóch zestawów słabych odniesień. Tym, czego chcesz uniknąć za pomocą bloków, jest cykl zatrzymania - dwa obiekty niepotrzebnie utrzymujące się nawzajem przy życiu.

Jeśli mam obiekt z tej właściwości:

@property (strong) void(^completionBlock)(void); 

i mam tej metody:

- (void)doSomething 
{ 
    self.completionBlock = ^{ 
     [self cleanUp]; 
    }; 

    [self doLongRunningTask]; 
} 

blok będzie utrzymywane przy życiu, kiedy należy go przechowywać w nieruchomości completionBlock. Ale ponieważ odwołuje się ona do self wewnątrz bloku, blok będzie utrzymywał przy życiu self, dopóki nie zniknie - ale tak się nie stanie, ponieważ oba odnoszą się do siebie nawzajem.

W tej metodzie:

- (void)doSomething 
{ 
    [[NSOperationQueue mainQueue] addOperationWithBlock:^{ 
     [self cleanUp]; 
    }]; 

    [self doLongRunningTask]; 
} 

nie trzeba zrobić słabą odniesienie do self. Blok będzie utrzymywał przy życiu self, ponieważ odwołuje się on do self od wewnątrz, ale ponieważ wszystko, co robimy, przekazuje blok do [NSOperationQueue mainQueue], self nie utrzymuje żywego bloku.

Mam nadzieję, że to pomoże.

22

Obie konstrukcje są w porządku. To zależy od twojego zamiaru. Co chcesz zrobić, jeśli obiekt zostanie (a) zwolniony po rozpoczęciu bloku zewnętrznego, ale (b) przed rozpoczęciem bloku wewnętrznego w głównej kolejce? Jeśli nie chcesz, aby został zachowany w tym scenariuszu (co domyślam się było twoim zamiarem, biorąc pod uwagę, że najpierw przechodzisz przez to ćwiczenie weakSelf), użyj ostatniego przykładu, w którym masz drugi słaby wskaźnik. W przeciwnym razie możesz użyć swojego innego przykładu.

Mimo, że kilka uwag:

  1. To nie jest utracone wniosek, że trzeba korzystać z tego weakSelf wzorzec w pierwszej kolejności. Niektórzy ludzie błędnie myślą, że mają mieć, aby używać tego wzoru weakSelf, aby uniknąć silnego cyklu odniesienia (cykl zatrzymania a.k.a.). Ale ta próbka kodu nie stanowi silnego cyklu odniesienia. Po prostu zachowuje obiekt podczas wykonywania kodu, co jest bardzo odmienne.

    W rzeczywistości czasami tego potrzebujesz/chcesz. Czasami nie. To zależy od problemu biznesowego, który rozwiązujesz. Oczywiście, często nie chcesz, aby zachować silne odniesienie do self, w takim przypadku wzór weakSelf ma sens. Ale nie zawsze tak jest.

    Ale chodzi mi o to, że nie powinieneś wykonywać tego wzorca weakSelf (przynajmniej w tym scenariuszu dispatch_async), aby uniknąć silnego cyklu odniesienia. Brak takiego cyklu. W takim przypadku występuje zmienna blokowa (np. Blok o wartości completionHandler). W takim przypadku wzór weakSelf jest krytyczny. Ale nie tutaj.

  2. Ale zastanówmy się przez chwilę nad tym scenariuszem, w którym nie chcesz zachować wartości self. Następnie pojawia się pytanie, czy w ogóle chcesz, aby kod wysyłania był kontynuowany. Jeśli nie, być może powinieneś używać kolejki operacji z operacjami do anulowania zamiast GCD.

    Na przykład, jestem zaskoczony, jak często ludzie dręczą się, czy zamierzają zachować kontroler widoku, podczas gdy niektóre żądania sieci w tle są uruchomione, ale nie martw się, czy powinny one anulować to żądanie sieci w tle pierwsze miejsce. Często ta ostatnia jest o wiele bardziej znacząca pod względem projektu (np. Pobrany plik PDF lub obraz zajmuje znacznie więcej zasobów systemowych (zarówno pamięci, jak i przepustowości sieci) niż kiedykolwiek kontroler widoku).

  3. Załóżmy jednak, że (a) naprawdę chcesz, aby wysłany kod nadal działał, ale (b) nie chcesz zachować wartości self. (Wydaje się to rzadkim scenariuszem, ale jest to ten, o który prosiłeś, więc przejdźmy dalej.) Ostateczne pytanie, czy potrzebujesz również swojej konstrukcji strongSelf. W twoim przypadku, gdy nazywasz tylko jedną metodę self, nie musisz zawracać sobie głowy tym konstruktem strongSelf. Jest to krytyczne tylko wtedy, gdy idziesz do szacunku ivars lub w inny sposób, aby uniknąć warunków wyścigu. Ale w tym przykładzie, biorąc pod uwagę, że wiadomość wysłana do obiektu nil nic nie robi, technicznie często nie musisz się w ogóle martwić o ten konstrukt strongSelf.

Nie zrozum mnie źle. Dobrze jest trzymać ramiona wokół wzoru weakSelf, a także zagnieżdżonego wzoru, który czasami mu towarzyszy. Po prostu sugeruję, że dobrze jest zrozumieć, kiedy te wzory są naprawdę potrzebne. I myślę, że wybór GCD w porównaniu z anulowanym NSOperation jest często o wiele bardziej krytycznym, ale często pomijanym pytaniem.

3

Bloki są tworzone i przechowywane na stosie. Tak więc blok zostanie zniszczony, gdy powróci metoda, która utworzyła blok.

Jeśli blok staje się zmienną instancji ARC, skopiuj blok ze stosu do sterty. Możesz jawnie skopiować blok z kopią wiadomości. Twój blok jest teraz blokiem opartym na sterty, a nie blokiem stosowym. I musisz poradzić sobie z niektórymi problemami z zarządzaniem pamięcią. Sam blok będzie utrzymywał silne odniesienie do wszelkich obiektów, do których się odwołuje. Deklaruj __weak wskaźniki poza blokiem, a następnie odwołaj się do tego wskaźnika w bloku, aby uniknąć cykli zatrzymania.