Question

J'utilise des blocs depuis un certain temps maintenant, mais j'ai l'impression qu'il y a des choses qui me manquent dans la gestion de la mémoire dans les environnements ARC et non-ARC.Je pense qu’une compréhension plus profonde me permettra d’éviter de nombreuses fuites de mémoire.

AFNetworking est ma principale utilisation des blocs dans une application particulière.La plupart du temps, dans un gestionnaire d'achèvement d'une opération, je fais quelque chose comme "[self.myArray addObject]".

Dans les environnements compatibles ARC et non-ARC, « soi » sera conservé en fonction de cet article d'Apple.

Cela signifie que chaque fois qu'un bloc d'achèvement d'une opération réseau AFNetworking est appelé, self est conservé à l'intérieur de ce bloc et libéré lorsque ce bloc sort de la portée.Je crois que cela s'applique à la fois à l'ARC et aux non-ARC.J'ai exécuté à la fois l'outil Leaks et l'analyseur statique afin de pouvoir détecter d'éventuelles fuites de mémoire.Aucun n’en a montré.

Cependant, ce n'est que récemment que je suis tombé sur un avertissement que je n'arrivais pas à comprendre.J'utilise ARC dans cet exemple particulier.

J'ai deux variables d'instance qui indiquent l'achèvement et l'échec d'une opération réseau

@property (nonatomic, readwrite, copy) SFCompletionBlock completionBlock;
@property (nonatomic, readwrite, copy) SFFailureBlock failureBlock;
@synthesize failureBlock = _failureBlock;
@synthesize operation = _operation;

Quelque part dans le code, je fais ceci :

[self.operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id
                                                    responseObject) {
NSError *error = [NSError errorWithDomain:@"com.test" code:100 userInfo:@{@"description": @"zero results"}];
            _failureBlock(error);
        } failure:^(AFHTTPRequestOperation *operation, NSError *error) {
            NSLog(@"nothing");
        }];

Xcode se plaint de la ligne qui appelle le FailureBlock, avec le message "Capturer fortement "self" dans ce bloc est susceptible d'entraîner un cycle de conservation.Je pense que Xcode a raison :le bloc d'échec conserve self et self détient sa propre copie du bloc, donc aucun des deux ne sera désalloué.

Cependant, j'ai les questions/observations suivantes.

1) Si je change _failureBlock(error) en "self.failureBlock(error)" (sans les guillemets), le compilateur cesse de se plaindre.Pourquoi donc?Est-ce une fuite de mémoire manquée par le compilateur ?

2) En général, quelle est la meilleure pratique pour travailler avec des blocs dans des environnements compatibles ARC et non ARC lors de l'utilisation blocs qui sont des variables d'instance?Il semble que dans le cas des blocs d'achèvement et d'échec dans AFNetworking, ces deux blocs sont pas variables d'instance, elles n'entrent donc probablement pas dans la catégorie des cycles de conservation que j'ai décrit ci-dessus.Mais lors de l’utilisation de blocs de progression dans AFNetworking, que peut-on faire pour éviter des cycles de conservation comme celui ci-dessus ?

J'aimerais entendre les réflexions d'autres personnes sur ARC et non-ARC avec des blocs et des problèmes/solutions avec la gestion de la mémoire.Je trouve que ces situations sont sujettes aux erreurs et j'estime qu'une discussion à ce sujet est nécessaire pour clarifier les choses.

Je ne sais pas si c'est important, mais j'utilise Xcode 4.4 avec le dernier LLVM.

Était-ce utile?

La solution

1) Si je change _failureBlock (erreur) sur "Self.failureBlock (erreur)" (Sans citations) Le compilateur cesse de se plaindre. Pourquoi donc? Est-ce une mémoire fuite le compilateur manque?

Le cycle de conservation existe dans les deux cas. Si vous ciblez iOS 5+, vous pouvez transmettre une référence faible à vous-même:

__weak MyClass *weakSelf;
[self.operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
    NSError *error = [NSError errorWithDomain:@"com.test" code:100 userInfo:@{@"description": @"zero results"}];
    if (weakSelf.failureBlock) weakSelf.failureBlock(error);
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
    NSLog(@"nothing");
}];

Maintenant Soi Soi Soi-même ne sera pas conservée et si elle est traitée avant que le rappel soit invoqué, le rappel est un non-op. Toutefois, il est possible que cela puisse subir une récipient pendant que le rappel est invoqué sur un thread de fond, ce modèle peut donc créer des accidents occasionnels.

2) En général, quelle est la meilleure pratique de travailler avec des blocs dans les deux Environnements activés par l'arc et les non-arc lors de l'utilisation de blocs qui sont variables d'instance? Semble qu'en cas d'achèvement et d'échec des blocs dans Afnetworking, ces deux blocs ne sont pas des variables d'instance afin ils ne tombent probablement pas dans la catégorie des cycles que je décrit ci-dessus. Mais lors de l'utilisation de blocs de progression dans Afnetworking, Que peut-on faire pour éviter de conserver des cycles comme celui ci-dessus?

la plupart du temps, je pense Il vaut mieux ne pas Magasin de blocs par exemple les variables . Si vous renvoyez plutôt le bloc d'une méthode de votre classe, vous aurez toujours un cycle de conserver, mais il n'existe que du temps que la méthode est appelée au moment où le bloc est libéré. Il évitera donc que votre instance d'être traitée lors de l'exécution du bloc, mais le cycle de retenue se termine lorsque le bloc est libéré:

-(SFCompletionBlock)completionBlock {
    return ^(AFHTTPRequestOperation *operation , id responseObject ) {
        [self doSomethingWithOperation:operation];
    };
}

[self.operation setCompletionBlockWithSuccess:[self completionBlock]
                                      failure:[self failureBlock]
];

Autres conseils

Cela signifie que chaque fois qu'un bloc d'achèvement d'une opération de réseau AFNetworking est appelé, Self est conservé à l'intérieur de ce bloc et libéré lorsque ce bloc sort de la portée.

Non, self est conservé par le bloc lors de la création du bloc.Et il est libéré lorsque le bloc est libéré.

Je pense que Xcode a raison :Le bloc de défaillance se conserve et se détient sa propre copie du bloc, donc aucun des deux ne sera traité.

Le bloc en question qui retient self est le bloc d'achèvement transmis à setCompletionBlockWithSuccess. self ne contient pas de référence à ce bloc.Plutôt, self.operation (sans doute une sorte de NSOperation) conserve les blocs pendant son exécution.Il y a donc temporairement un cycle.Cependant, une fois l’exécution de l’opération terminée, le cycle sera interrompu.

1) Si je modifie _failureBlock (erreur) sur "self.failureBlock (erreur)" (sans citations), le compilateur cesse de se plaindre.Pourquoi donc?Est-ce une fuite de mémoire que le compilateur manque?

Il ne devrait y avoir aucune différence. self est capturé dans les deux cas.Il n'est pas garanti que le compilateur détecte tous les cas de cycles de conservation.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top