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);
    }
}
Author: JOM, 2013-11-17

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];
}];
 727
Author: jemmons,
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];
}];
 28
Author: Ilker Baltaci,
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.

 26
Author: Leo Natan,
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.:

liczba rekordów w Xcode 6

Lub w Xcode 5:

liczba rekordów w Xcode 5

 19
Author: Rob,
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

 0
Author: Danyun Liu,
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";


};
 -2
Author: Ranjeet Singh,
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