Używanie słabego self w funkcji asynchronicznej wysyłania
Czytałem wiele postów o używaniu __weak self
wewnątrz dispatch_async
, a teraz jestem trochę zdezorientowany.
Jeśli mam:
self.myQueue = dispatch_queue_create("com.biview.core_data", NULL);
dispatch_async(self.myQueue, ^(void){
if (!self.var1) {
self.var1 = ...;
}
dispatch_async(dispatch_get_main_queue(), ^(void) {
if ([self.var2 superview]) {
[self.var2 removeFromSuperview];
}
[self.Label setText:text];
});
});
Czy muszę używać __weak self
. Ponieważ czytałem, że w niektórych przypadkach dispatch_async
nie trzeba __weak self
.
2 answers
Zakładając, self jest wskaźnikiem obiektu do UIViewController
.
Rzeczy do rozważenia:
A
UIViewController
jest obiektem "UIKit". Obiekty UIKit nie mogą być wysyłane metodami w wątkach innych niż główne, czyli - metody te muszą być wykonywane tylko w głównym wątku!Blok, który został zapytany w kolejce - czy to był synchronicznie czy asynchronicznie - zostanie ostatecznie wykonany - bez względu na wszystko! No chyba, że program się zakończy zanim to się stanie.
Przechwycone retainable silne wskaźniki zostaną zachowane , gdy blok zostanie skopiowany (na przykład, gdy zostanie wysłany asynchronicznie), i ponownie zwolnione , gdy blok zostanie zniszczony (po jego zakończeniu).
Przechwycone utrzymywalne słabe wskaźniki nie zostaną zatrzymane i nie zostaną zwolnione.
W Twoim scenariuszu, gdzie uchwycisz siebie w bloku, który jest wysyłane w kolejce głównej , nie musisz się martwić, że złe rzeczy się zdarzają.
Więc dlaczego? A co się właściwie dzieje?Ponieważ self zostanie przechwycony w bloku, który jest wysyłany asynchronicznie, self zostanie w domyśle zatrzymane, a zwolnione ponownie po zakończeniu bloku.
Oznacza to, że czas życia siebie będzie przedłużony aż do wykończenia bloków. Zauważ, że twój drugi blok jest wysyłany do głównego wątku i masz gwarancję, że self będzie nadal żywy, gdy blok zostanie wykonany.
Powyższa "przedłużona żywotność" może być pożądaną cechą twojego programu.
Jeśli jawnie nie chcesz przedłużać czasu życia obiektu UIViewController
, a zamiast tego chcesz, aby blok-kiedy w końcu zostanie wykonany - sprawdź czy ten obiekt {6]} nadal istnieje w wszystko, można użyć _ _ słaby wskaźnik jaźni. Zauważ, że blok zostanie ostatecznie wykonany, bez względu na to, czy UIViewController
jest nadal żywy, czy został dealokowany w międzyczasie.
Możesz chcieć, aby blok robił "nic", Jeśli UIViewController
został dealokowany przed blok zostanie wykonany:
MyController* __weak weakSelf = self;
dispatch_async(queue, ^{
MyController* strongSelf = weakSelf;
if (strongSelf) {
...
}
else {
// self has been deallocated in the meantime.
}
});
Zobacz także: przejście do informacji o wydaniu ARC
Pamiętaj: UIKit
obiekty nie powinny być wysyłane metodami w wątkach innych niż główne!
Jeden inny subtelny błąd może wynikać z faktu, że UIKit
obiekty powinny wykonywać metody tylko w głównym wątku.
UIKit
, który jest wysyłany asynchronicznie i jest wykonywany na Nie-głównym wątku. Wtedy może się zdarzyć, że blok zawiera ostatni silne odniesienie do tego UIKit
obiektu. Teraz, gdy blok zostanie ostatecznie wykonany, blok zostanie zniszczony, a obiekt UIKit
zostanie zwolniony. Ponieważ jest to ostatni silny odniesienie do UIKit
obiektu, zostanie on dealokowany. Dzieje się tak jednak w wątku, w którym blok został wykonany - i to nie jest główny wątek! Teraz złe rzeczy mogą (i zwykle będą) się dziać, ponieważ metoda dealloc
nadal jest metodą wysyłaną do obiektu UIKit
.
Można uniknąć tego błędu, wysyłając blok przechwytujący silny wskaźnik do tego obiektu UIKit i wysyłając do niego metodę atrapy:
UIViewController* strongUIKitPointer = ...
dispatch_async(non_main_queue, ^{
... // do something
dispatch(dispatch_get_main_queue(), ^{
[strongUIKitPointer self]; // note: self is a method, too - doing nothing
});
});
W Twoim scenariuszu, ostatni strong reference może być tylko w bloku, który wykonuje się w głównym wątku. Więc jesteście bezpieczni od tego subtelnego błędu. ;)
Edit:
W konfiguracji nigdy nie masz cyklu zachowywania. Cykl zachowywania występuje, gdy obiekt A silnie odwołuje się do innego obiektu B, A obiekt B silnie odwołuje się do A. zauważ, że "blok" jest również obiektem dającym się utrzymać.
Wymyślony przykład z cyklicznym odniesieniem:
typedef void(^my_completion_block_t)(NSArray* result);
@interface UsersViewController : UIViewController
@property (nonatomic, copy) my_completion_block_t completion;
@property (nonatomic) NSArray* users;
@end
Tutaj mamy nieruchomość dopełnienie którego typem wartości jest blok. Oznacza to, że otrzymujemy ivar o nazwie _completion
, którego typem jest blok.
Klient może ustawić obsługę zakończenia, która powinna być wywołana po zakończeniu określonej operacji. Załóżmy, że operacja pobiera listę użytkowników ze zdalnego serwera. Plan polega na ustawieniu właściwości users Po zakończeniu operacji:
Nieostrożne podejście przypadkowo wprowadziłoby cykliczne odniesienie:
Gdzieś w "UsersViewController.m "
self.completion = ^(NSArray* users){
self.users = users;
}
[self fetchUsers]; // start asynchronous task
Tutaj, self posiada silne odniesienie do ivar _completion
, który jest blokiem. I sam blok przechwytuje self, co powoduje zachowanie self, gdy blok zostanie skopiowany, gdy zostanie wysłany. To klasyczny cykl odniesienia.
-
Używając
__weak
kwalifikowanego wskaźnika selfUsersViewController* __weak weakSelf = self; self.completion = ^(NSArray* users) { UsersViewController* strongSelf = weakSelf; if (strongSelf) { strongSelf.users = users; } else { // the view controller does not exist anymore } } [usersViewController fetchUsers];
-
Użycie
__block
kwalifikowanego wskaźnika self i ostatecznie ustawienie gonil
w bloku po jego zakończeniu:UsersViewController* __block blockSelf = self; self.completion = ^(NSArray* users) { blockSelf.users = users; blockSelf = nil; } [usersViewController fetchUsers];
Zobacz także: przejście do informacji o wydaniu ARC
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-02-25 12:32:14
Szybka aktualizacja:
Przykład tego tzw. silnego-słabego tańca w języku swift:
func doSomeThingAsynchronously() {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)) { () -> () in
// Do task in default queue
dispatch_async(dispatch_get_main_queue(), { [weak self] () -> () in
guard let strongSelf = self else { return }
// Do task in main queue
strongSelf.updateView()
})
}
}
Popularny projekt open source Alamofire
wykorzystuje to podejście.
Przedłużyć żywotność obiektu używając idiomu [weak self] i guard let strongSelf = self else {return}.
Aby uzyskać więcej informacji sprawdź swift-style-guide
Swift 3 update:
func doSomeThingAsynchronously() {
DispatchQueue.global().async {
// Do task in default queue
DispatchQueue.main.async { [weak self] in
// Do task in main queue
guard let strongSelf = self else { return }
strongSelf.updateView()
}
}
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-10-05 08:44:58