Jestem trochę zdezorientowany co do użycia bloku w Objective-C. Obecnie używam ARC i mam dość dużo bloków w mojej aplikacji, obecnie zawsze odnoszących się do self
zamiast jej słabego odniesienia. Czy może to być przyczyną zatrzymania self
i zablokowania bloków przed zwolnieniem? Pytanie brzmi: czy zawsze powinienem używać weak
odniesienia self
w bloku?
-(void)handleNewerData:(NSArray *)arr
{
ProcessOperation *operation =
[[ProcessOperation alloc] initWithDataToProcess:arr
completion:^(NSMutableArray *rows) {
dispatch_async(dispatch_get_main_queue(), ^{
[self updateFeed:arr rows:rows];
});
}];
[dataProcessQueue addOperation:operation];
}
ProcessOperation.h
@interface ProcessOperation : NSOperation
{
NSMutableArray *dataArr;
NSMutableArray *rowHeightsArr;
void (^callback)(NSMutableArray *rows);
}
ProcessOperation.m
-(id)initWithDataToProcess:(NSArray *)data completion:(void (^)(NSMutableArray *rows))cb{
if(self =[super init]){
dataArr = [NSMutableArray arrayWithArray:data];
rowHeightsArr = [NSMutableArray new];
callback = cb;
}
return self;
}
- (void)main {
@autoreleasepool {
...
callback(rowHeightsArr);
}
}
ios
iphone
objective-c
automatic-ref-counting
weak-references
the_critic
źródło
źródło
Odpowiedzi:
Pomaga nie koncentrować się na dyskusji
strong
ani jejweak
części. Zamiast tego skup się na części cyklu .Cykl zatrzymania to pętla, która zachodzi, gdy Obiekt A zachowuje Obiekt B, a Obiekt B zachowuje Obiekt A. W takiej sytuacji, jeśli dowolny obiekt zostanie zwolniony:
Tak więc te dwa obiekty będą po prostu wisiały w pamięci przez cały czas trwania programu, mimo że powinny, jeśli wszystko działałoby poprawnie, zostać zwolnione.
Dlatego martwimy się o zachowanie cykli , a same bloki nie mają nic wspólnego z tymi cyklami. To nie jest problem, na przykład:
Blok zachowuje
self
, aleself
nie zachowuje bloku. Jeśli jedno lub drugie zostanie zwolnione, cykl nie zostanie utworzony i wszystko zostanie zwolnione tak, jak powinno.W kłopoty jest coś takiego:
Teraz twoja
self
funkcja ( ) ma jawnestrong
odniesienie do bloku. I blok ma ukryte silne odniesienie doself
. To cykl, a teraz żaden obiekt nie zostanie odpowiednio zwolniony.Ponieważ w takiej sytuacji
self
z definicji istnieje jużstrong
odwołanie do bloku, zwykle najłatwiej go rozwiązać, tworząc wyraźnie słabe odwołanie doself
bloku:Ale nie powinien to być domyślny wzorzec, który stosujesz w przypadku bloków, które wywołują
self
! Powinno to być użyte tylko do przerwania tego, co w innym przypadku byłoby cyklem zatrzymania między sobą a blokiem. Jeśli wszędzie zastosujesz ten wzorzec, ryzykujesz przekazaniem bloku do czegoś, co zostało wykonane poself
zwolnieniu.źródło
-setCompleteionBlockWithSuccess:failure:
metody. Ale jeślipaginator
jest własnościąViewController
, a te bloki nie zostaną wywołane poViewController
, zostaną zwolnione, użycie__weak
odniesienia byłoby bezpiecznym ruchem (ponieważself
jest właścicielem rzeczy, która jest właścicielem bloków, a więc prawdopodobnie nadal będzie w pobliżu, gdy wywołają je bloki nawet jeśli tego nie zachowują). Ale to dużo „jeśli”. To naprawdę zależy od tego, co to ma zrobić.MyObject
iSomeOtherObject
oboje są właścicielami bloku. Ale ponieważ blok jest odesłanie doMyObject
toweak
blok nie własnyMyObject
. Więc gdy blok jest gwarantowana istnieć tak długo, jak alboMyObject
czySomeOtherObject
istnieje, nie ma gwarancji, żeMyObject
będzie istnieć tak długo, jak blok robi.MyObject
można całkowicie zwolnić i tak długo, jak długoSomeOtherObject
istnieje, blok będzie nadal istniał.Nie zawsze musisz używać słabego odniesienia. Jeśli twój blok nie zostanie zachowany, ale zostanie wykonany, a następnie odrzucony, możesz mocno przechwycić siebie, ponieważ nie utworzy to cyklu zatrzymania. W niektórych przypadkach chcesz nawet, aby blok utrzymywał siebie do momentu ukończenia bloku, aby nie przedwcześnie zwolnił. Jeśli jednak silnie przechwycisz blok, a wewnątrz siebie przechwytywanie utworzy cykl zatrzymania.
źródło
Całkowicie zgadzam się z @jemmons:
Aby rozwiązać ten problem, można zdefiniować silne odniesienie
weakSelf
wewnątrz bloku:źródło
->
), w którym chcesz zagwarantować, że faktycznie masz ważne odwołanie i utrzymujesz je w sposób ciągły przez cały zestaw operacji, np.if ( strongSelf ) { /* several operations */ }
Jak zauważa Leo, kod dodany do pytania nie sugerowałby silnego cyklu odniesienia (inaczej: zachowaj cykl). Jednym z problemów związanych z operacją, które mogłyby spowodować silny cykl odniesienia, byłby brak zwolnienia operacji. Chociaż fragment kodu sugeruje, że nie zdefiniowałeś operacji jako współbieżnej, ale jeśli tak, to nie zostanie zwolniony, jeśli nigdy nie opublikujesz wiadomości
isFinished
, nie będziesz mieć zależności cyklicznych lub czegoś podobnego. A jeśli operacja nie zostanie zwolniona, kontroler widoku również nie zostanie zwolniony. Sugerowałbym dodanie punktu przerwania lub metodyNSLog
operacjidealloc
i potwierdzenie, że zostanie wywołany.Powiedziałeś:
Problemy z cyklem przechowywania (silny cykl odniesienia) występujące w blokach są podobne do problemów z cyklem przechowywania, które znasz. Blok zachowa silne odwołania do wszelkich obiektów pojawiających się w bloku i nie zwolni tych silnych odniesień, dopóki sam blok nie zostanie zwolniony. Tak więc, jeśli odwołania do bloku
self
, a nawet po prostu odwołują się do zmiennej instancjiself
, która zachowa silne odniesienie do siebie, to nie zostanie rozwiązane, dopóki blok nie zostanie zwolniony (lub w tym przypadku, dopóki nieNSOperation
zostanie zwolniona podklasa).Aby uzyskać więcej informacji, zobacz temat Unikanie silnych cykli odniesienia podczas przechwytywania części własnej programowania z dokumentem Cel-C: Praca z blokami .
Jeśli kontroler widoku nadal nie jest uwalniany, musisz po prostu określić, gdzie znajduje się nierozwiązane silne odwołanie (zakładając, że potwierdziłeś, że
NSOperation
jest on zwolniony). Typowym przykładem jest użycie powtarzaniaNSTimer
. Lub jakiś niestandardowydelegate
lub inny obiekt, który błędnie utrzymujestrong
odniesienie. Często można używać przyrządów do śledzenia miejsc, w których obiekty uzyskują silne referencje, np .:Lub w Xcode 5:
źródło
Niektóre wyjaśnienia ignorują warunek dotyczący cyklu przechowywania [Jeśli grupa obiektów jest połączona kręgiem silnych relacji, utrzymują się one przy życiu, nawet jeśli nie ma żadnych silnych referencji spoza grupy.] Aby uzyskać więcej informacji, przeczytaj dokument
źródło
Oto jak możesz użyć self wewnątrz bloku:
// wywołanie bloku
źródło