web-dev-qa-db-fra.com

Toujours passer la référence faible de soi dans le bloc dans ARC?

Je suis un peu confus au sujet de l'utilisation des blocs dans Objective-C. J'utilise actuellement ARC et j'ai beaucoup de blocs dans mon application, faisant toujours référence à self au lieu de sa faible référence. Est-ce que cela peut être la cause de ces blocages conservant self et l'empêchant d'être désalloués? La question est, devrais-je toujours utiliser une référence weak de self dans un bloc?

-(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);
    }
}
246
the_critic

Il est préférable de ne pas se concentrer sur la partie strong ou weak de la discussion. Concentrez-vous plutôt sur la partie du cycle .

Un cycle de retenue est une boucle qui se produit lorsque l'objet A conserve l'objet B, et L'objet B conserve l'objet A. Dans cette situation, si l'un des objets est libéré:

  • L'objet A ne sera pas désalloué car l'objet B contient une référence.
  • Mais l'objet B ne sera jamais désalloué tant que l'objet A sera référencé.
  • Mais l’objet A ne sera jamais désalloué car l’objet B en contient une référence.
  • à l'infini

Ainsi, ces deux objets resteront en mémoire pendant toute la durée du programme, même s’ils devraient, si tout fonctionnait correctement, être désalloués.

Donc, ce qui nous inquiète, c’est de conserver des cycles , et il n’ya rien sur les blocs en eux-mêmes qui créent ces cycles. Ce n'est pas un problème, par exemple:

[myArray enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop){
   [self doSomethingWithObject:obj];
}];

Le bloc conserve self, mais self ne conserve pas le bloc. Si l'un ou l'autre est libéré, aucun cycle n'est créé et tout est désalloué comme il se doit.

Où vous avez des problèmes est quelque chose comme:

//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];     
}];

Maintenant, votre objet (self) a une référence explicite strong au bloc. Et le bloc a une forte référence implicite à self. C'est un cycle, et maintenant aucun des objets ne sera désalloué correctement.

Parce que, dans une situation comme celle-ci, self par définition a déjà une référence strong au bloc, il est généralement plus facile de résoudre en faisant une référence explicitement faible à self pour le bloc à utiliser:

__weak MyObject *weakSelf = self;
[self setMyBlock:^(id obj, NSUInteger idx, BOOL *stop) {
  [weakSelf doSomethingWithObj:obj];     
}];

Mais ce ne devrait pas être le motif par défaut que vous suivez pour les blocs qui appellent self! Ceci ne devrait être utilisé que pour rompre ce qui serait autrement un cycle de conservation entre soi et le bloc. Si vous adoptiez ce modèle partout, vous courriez le risque de passer un bloc à quelque chose qui aurait été exécuté après la désallocation de 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];
}];
700
jemmons

Vous n'avez pas toujours à utiliser une référence faible. Si votre bloc n'est pas conservé, mais exécuté puis supprimé, vous pouvez vous capturer vous-même, car cela ne créera pas de cycle de conservation. Dans certains cas, vous souhaitez même que le bloc conserve son autonomie jusqu'à la fin du blocage afin qu'il ne soit pas désalloué prématurément. Si, toutefois, vous capturez fortement le bloc et si vous capturez vous-même, un cycle de rétention sera créé.

25
Leo Natan

Je suis totalement d'accord avec @jemmons.

"Mais cela ne devrait pas être le modèle par défaut que vous suivez lorsque vous traitez avec des blocs qui s'appellent eux-mêmes! Cela ne devrait être utilisé que pour rompre ce qui serait autrement un cycle de conservation entre soi et le bloc. Si vous deviez adopter ce modèle partout, vous ' d courez le risque de passer un bloc à quelque chose qui a été exécuté après la désallocation de soi-même. "

//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];
}];

Pour résoudre ce problème, on peut définir une référence forte sur le faible soi-même à l'intérieur du bloc.

__weak MyObject *weakSelf = self;
[[SomeOtherObject alloc] initWithCompletion:^{
   MyObject *strongSelf = weakSelf;
  [strongSelf doSomething];
}];
23
Ilker Baltaci

Comme Leo le fait remarquer, le code que vous avez ajouté à votre question ne suggérerait pas un cycle de référence fort (cycle de maintien). Un problème lié à l'opération qui pourrait entraîner un cycle de référence important serait celui-ci si l'opération n'était pas libérée. Bien que votre extrait de code indique que vous n'avez pas défini votre opération comme étant simultanée, elle ne serait pas publiée si vous n'aviez jamais posté isFinished, ou si vous aviez des dépendances circulaires, ou quelque chose du genre. Et si l'opération n'est pas validée, le contrôleur de vue ne le serait pas non plus. Je suggérerais d'ajouter un point d'arrêt ou NSLog dans la méthode dealloc de votre opération et de confirmer que l'appelant a été effectué.

Tu as dit:

Je comprends la notion de cycles de conservation, mais je ne suis pas tout à fait sûr de ce qui se passe dans les blocs, ce qui me confond un peu.

Les problèmes de cycle de conservation (cycle de référence élevé) qui se produisent avec les blocs sont similaires aux problèmes de cycle de conservation que vous connaissez bien. Un bloc conservera des références fortes à tous les objets apparaissant dans le bloc, et il ne libérera pas ces références fortes tant que le bloc ne sera pas libéré. Ainsi, si les références de bloc self, ou même simplement une référence à une variable d'instance de self, conservera une référence forte à self, qui ne sera pas résolue tant que le bloc ne sera pas libéré (ou dans ce cas, NSOperation la sous-classe est libérée.

Pour plus d'informations, reportez-vous à la section Évitez les cycles de référence importants lors de la capture de votre propre compte de la Programmation avec Objective-C: Utilisation de blocs document.

Si votre contrôleur de vue n'est toujours pas libéré, vous devez simplement identifier l'emplacement de la référence forte non résolue (en supposant que vous confirmiez que la NSOperation est en train d'être désallouée). Un exemple courant est l'utilisation d'une répétition NSTimer. Ou un objet personnalisé delegate ou un autre objet qui conserve par erreur une référence strong. Vous pouvez souvent utiliser Instruments pour rechercher où les objets obtiennent leurs références fortes, par exemple:

record reference counts in Xcode 6

Ou dans Xcode 5:

record reference counts in Xcode 5

19
Rob

Certaines explications ignorent une condition relative au cycle de conservation [Si un groupe d’objets est connecté par un cercle de relations solides, ils se gardent mutuellement en vie même s’il n’existe aucune référence forte en dehors du groupe.] Pour plus d’informations, lisez la - document

0
Danyun Liu