Zawsze przekazać słabe odniesienie siebie do bloku w łuku?
Jestem trochę zdezorientowany co do użycia bloków w Objective-C. obecnie używam ARC i mam sporo bloków w mojej aplikacji, Obecnie zawsze odwołując się do self
zamiast do jego słabego odniesienia. Czy to może być przyczyną tego, że te bloki zatrzymują self
i powstrzymują je przed deallocowaniem ? Pytanie brzmi, Czy zawsze powinienem używać weak
referencji 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];
}
Procesoperacji.h
@interface ProcessOperation : NSOperation
{
NSMutableArray *dataArr;
NSMutableArray *rowHeightsArr;
void (^callback)(NSMutableArray *rows);
}
Procesoperacji.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);
}
}
6 answers
Pomaga nie skupiać się na strong
lub weak
części dyskusji. Zamiast tego skup się na części cyklu.
A zachowuje cykl jest pętlą, która dzieje się, gdy obiekt A zachowuje obiekt B, i obiekt B zachowuje obiekt A. w takiej sytuacji, jeśli jeden z obiektów jest zwolniony:
- obiekt A nie zostanie dealokowany, ponieważ obiekt B posiada odniesienie do niego.
- Ale obiekt B nigdy nie będzie dealokowany tak długo, jak obiekt A ma odniesienie do to.
- Ale obiekt A nigdy nie zostanie dealokowany, ponieważ obiekt B posiada odniesienie do niego.
- ad infinitum
Tak więc te dwa obiekty będą po prostu wisieć w pamięci przez całe życie programu, nawet jeśli powinny, jeśli wszystko działało prawidłowo, zostać rozdzielone.
Więc martwimy się o zachowanie cykli i nie ma nic o blokach samych w sobie, które tworzą te cykle. To nie jest problem, bo przykład:
[myArray enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop){
[self doSomethingWithObject:obj];
}];
Blok zachowuje self
, ale self
nie zachowuje bloku. Jeśli jeden lub drugi zostanie zwolniony, nie zostanie utworzony żaden cykl i wszystko zostanie rozdzielone tak, jak powinno.
Gdzie wpadasz w kłopoty to coś w stylu:
//In the interface:
@property (strong) void(^myBlock)(id obj, NSUInteger idx, BOOL *stop);
//In the implementation:
[self setMyBlock:^(id obj, NSUInteger idx, BOOL *stop) {
[self doSomethingWithObj:obj];
}];
Teraz Twój obiekt (self
) ma wyraźne odniesienie strong
do bloku. I blok ma implicit silne odniesienie do self
. To jest cykl, a teraz żaden z obiektów nie będzie odpowiednio dealokowany.
Ponieważ, w takiej sytuacji, self
z definicji ma już strong
odniesienie do bloku, Zwykle najłatwiej jest rozwiązać poprzez wyraźne słabe odniesienie do self
, aby użyć bloku:
__weak MyObject *weakSelf = self;
[self setMyBlock:^(id obj, NSUInteger idx, BOOL *stop) {
[weakSelf doSomethingWithObj:obj];
}];
Ale nie powinno to być domyślne wzorce, które stosujesz , gdy masz do czynienia z blokami, które wywołują self
! Powinno to być używane tylko do przerwania tego, co w przeciwnym razie byłoby cyklem zachowania między sobą a blokiem. Gdybyś zaadoptował ten wzór wszędzie, zaryzykowałbyś o przejściu bloku do czegoś, co zostało wykonane po dealokacji self
.
//SUSPICIOUS EXAMPLE:
__weak MyObject *weakSelf = self;
[[SomeOtherObject alloc] initWithCompletion:^{
//By the time this gets called, "weakSelf" might be nil because it's not retained!
[weakSelf doSomething];
}];
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
2013-11-17 14:55:46
Całkowicie się Zgadzam z @jemmons:
Ale to nie powinno być domyślnym wzorcem, który stosujesz, gdy masz do czynienia z blokami, które wywołują self! Powinno to być używane tylko do przerwania tego, co w przeciwnym razie byłoby cyklem zachowania między sobą a blokiem. Gdybyś wszędzie zaadoptował ten wzór, zaryzykowałbyś Przejście bloku do czegoś, co zostało stracone po tym, jak self został dealokowany.
//SUSPICIOUS EXAMPLE: __weak MyObject *weakSelf = self; [[SomeOtherObject alloc] initWithCompletion:^{ //By the time this gets called, "weakSelf" might be nil because it's not retained! [weakSelf doSomething]; }];
Aby przezwyciężyć ten problem można zdefiniować silne odniesienie nad weakSelf
wewnątrz bloku:
__weak MyObject *weakSelf = self;
[[SomeOtherObject alloc] initWithCompletion:^{
MyObject *strongSelf = weakSelf;
[strongSelf doSomething];
}];
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
2019-10-10 11:25:37
Nie musisz zawsze używać słabego odniesienia. Jeśli twój blok nie zostanie zachowany, ale zostanie wykonany, a następnie odrzucony, możesz mocno przechwycić swój blok, ponieważ nie utworzy to cyklu zachowywania. W niektórych przypadkach chcesz nawet, aby blok utrzymywał jaźń aż do zakończenia bloku, aby nie deallokował się przedwcześnie. Jeśli jednak mocno uchwycisz blok, a wewnątrz przechwycisz ja, utworzy to cykl zatrzymania.
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
2013-11-17 12:56:45
Jak wskazuje Leo, kod, który dodałeś do pytania, nie sugerowałby silnego cyklu odniesienia (a.k.a., retain cycle). Jednym z problemów związanych z operacją, który mógłby spowodować silny cykl odniesienia, byłoby, gdyby operacja nie została zwolniona. Podczas gdy twój fragment kodu sugeruje, że nie zdefiniowałeś swojej operacji jako współbieżnej, ale jeśli tak, nie zostanie ona wydana, jeśli nigdy nie opublikujesz isFinished
, lub jeśli masz okrągłe zależności, lub coś w tym stylu. A jeśli operacja nie jest wydany, kontroler widoku również nie zostanie wydany. Sugerowałbym dodanie punktu przerwania lub NSLog
w metodzie operacji dealloc
i potwierdzenie, że zostanie wywołany.
Powiedziałeś:
Rozumiem pojęcie cykli zatrzymywania, ale nie jestem do końca pewien, co dzieje się w blokach, więc trochę mnie to myli
Problemy z cyklem zachowywania (silny cykl odniesienia), które występują w blokach, są podobne do problemów z cyklem zachowywania, które znasz. A blok zachowa silne odniesienia do wszelkich obiektów, które pojawiają się w bloku, i nie wyda tych silnych odniesień, dopóki sam blok nie zostanie zwolniony. Tak więc, jeśli Block odwołuje się do self
, lub nawet odwołuje się do zmiennej instancji self
, to zachowa silne odniesienie do self, które nie zostanie rozwiązane dopóki blok nie zostanie zwolniony (lub w tym przypadku, dopóki podklasa NSOperation
nie zostanie zwolniona.
Aby uzyskać więcej informacji, zobacz unikanie silnych cykli odniesienia podczas przechwytywania self sekcja programowania z Objective-C: Praca z blokami dokumentu.
Jeśli kontroler widoku nadal nie zostanie zwolniony, musisz po prostu określić, gdzie znajduje się nierozwiązane silne odniesienie(zakładając, że potwierdziłeś, że NSOperation
jest dealokowany). Częstym przykładem jest użycie powtórzenia NSTimer
. Lub jakiś Niestandardowy delegate
lub inny obiekt, który błędnie utrzymuje odniesienie strong
. Często można użyć instrumentów, aby wyśledzić, gdzie znajdują się obiekty uzyskanie ich mocnych odniesień, np.:
Lub w Xcode 5:
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
2015-04-07 19:55:10
Niektóre wyjaśnienia ignorują warunek dotyczący cyklu zachowywania [jeśli grupa obiektów jest połączona kręgiem silnych relacji, utrzymują się nawzajem przy życiu, nawet jeśli nie ma silnych odniesień spoza grupy.] Aby uzyskać więcej informacji, przeczytaj Dokument
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
2013-11-17 17:07:21
Tak można używać jaźni wewnątrz bloku:
/ / wywołanie bloku
NSString *returnedText= checkIfOutsideMethodIsCalled(self);
NSString* (^checkIfOutsideMethodIsCalled)(*)=^NSString*(id obj)
{
[obj MethodNameYouWantToCall]; // this is how it will call the object
return @"Called";
};
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-08-31 11:15:57