web-dev-qa-db-fra.com

Fuite de mémoire Swift Struct

Nous essayons d'utiliser les structures Swift dans la mesure du possible. Nous utilisons également RxSwift qui a des méthodes qui prennent des fermetures. Quand nous avons une structure qui crée une fermeture qui fait référence à self , cela crée un cycle de référence fort .

import Foundation
import RxSwift

struct DoesItLeak {

    var someState: String = "initial value"
    var someVariable: Variable<String> = Variable("some stuff")

    let bag = DisposeBag()

    mutating func someFoo() {

        someVariable.subscribeNext { person in

            self.someState = "something"
        }
        .addDisposableTo(bag)
    }
}

Comment je sais ça? Si je crée 100 000 objets DoesItLeak et que j'appelle someFoo () sur chacun d'eux, je crois que j'ai 100 000 objets avec des cycles de référence puissants. En d'autres termes, lorsque je supprime le tableau DoesItLeak contenant ces objets, ceux-ci restent en mémoire. Si je n'appelle pas someFoo (), il n'y a pas de problème. 

La variable est une classe. Donc, je peux voir ce problème de mémoire en utilisant les Allocations Instruments de xcode et en filtrant dans Variable <String>  

 Filtering By Variable

 enter image description here

Si j'essaie d'utiliser [moi faible] comme dans ce qui suit, j'obtiens une erreur de compilation:

someVariable.subscribeNext { [weak self] person in

L'erreur du compilateur est "faible ne peut pas être appliqué à un type non-classe" 

En code réel/non-exemple, nous accédons à des méthodes et à des variables via self, c'est un problème de mémoire.

Comment puis-je résoudre ce problème de mémoire tout en conservant la structure DoesItLeak?  

Merci de votre aide.

22
finneycanhelp

Comme Darren , mettez-le dans les commentaires: " DoesItLeak ne peut pas être une structure " Nous ne pouvons pas avoir la DoesItLeak être une structure et résoudre en toute sécurité le problème du cycle de référence fort. 

Les types de valeur tels que les structures existent sur le cadre de la pile. Les fermetures et les classes sont des types de référence. 

Comme le dit le Section des cycles de référence puissants pour les fermetures :

Ce cycle de référence fort se produit car les fermetures, comme les classes, sont des types de référence. 

Étant donné que la structure a la valeur Variable class et que la fermeture faisant référence à self est stockée dans la classe Variable à l'aide de subscribeNext, elle crée le cycle de référence fort. Voir "Résolution des cycles de référence importants pour les fermetures" dans Comptage automatique des références Documentation Apple. 

5
finneycanhelp

Pour quiconque est toujours confronté à ce problème.

1) [weak self] n'est pas possible car Struct est value type et non un Reference type, donc pas de pointeur en tant que tel.

2) Le problème principal de la fuite est que vous essayez d'accéder à la propriété Struct self.someState = something à l'intérieur du bloc d'achèvement, ce qui créera une nouvelle copie de votre structure lors de l'affectation. 

Vous ne devez pas accéder à la propriété Struct à l'intérieur du bloc d'achèvement. 

0
helloWorld

Le schéma de capture de soi par une fermeture évasive dans un contexte accessible en écriture est maintenant interdit. Le compilateur Swift émettra une erreur "La fermeture ne peut pas capturer implicitement un paramètre auto en mutation". Si le contexte est en lecture seule, la valeur de self pourrait être copiée ou partagée et, dans les deux cas, il n'y aurait pas de cycle de référence.

0
Berik