ARC et Structures C, problème de fuite de mémoire avec malloc...

HerveHerve Membre

Bonjour,


 


D'après la doc, cela a l'air "normal" : il semble qu'il soit déconseillé d'utiliser des structures C sous iOS avec ARC.


Jusqu'à  présent, sous MacOS, je n'utilisais pas ARC. Les structures avaient un comportement très stable. Avec ARC, elles "fuient" à  peine initialisées.


Je fais la méthode 



monInstance = malloc(sizeof(maStructureC));

dans la méthode init. Pas de problème tant que j'utilise cette structure dans "init". Mais dès que j'appelle cette structure dans une autre méthode, elle a disparu...


 


J'ai implémenté les "free(monInstance)", méthode appelée depuis l'AppManager à  la fermeture, j'ai essayé de mettre "_strong" au moment des "typedef", etc.


 


J'ai lu ceci :


https://developer.apple.com/library/ios/releasenotes/objectivec/rn-transitioningtoarc/introduction/introduction.html


ou cela :


https://developer.apple.com/library/mac/documentation/performance/conceptual/managingmemory/articles/MemoryAlloc.html


 


Je crains devoir me passer de ces structures pourtant bien commodes (et devoir beaucoup réécrire mon code venant de MacOS au passage...), mais peut-être que quelqu'un "a un truc"??


 


Merci par avance dans ce cas!


Réponses

  • PyrohPyroh Membre

    Si je ne m'abuse les types opaques de CoreGraphics (CG*) sont bien des structures non ?


    Ils font comment eux ?


  • AliGatorAliGator Membre, Modérateur
    Pour les types opaques de CoreFoundation, cf la discussion dans l'autre thread concernant CFRelease.

    C'est vrai que le mieux c'est quand même d'avoir des objets.
    D'autant que maintenant, avec le property-auto-synthesis + ARC et tout le reste, ça a apporté beaucoup, et que maintenant une sous-classe de NSObject peut juste s'écrire :
    // .h
    @interface Person : NSObject
    @property(strong) NSString* firstName;
    @property(strong) NSString* lastName;
    @property(assign) int age;
    @end

    // .m
    @implementation Person @end
    Donc c'est aussi compact que de déclarer une structure Person équivalente.
  • HerveHerve Membre
    mai 2014 modifié #4

    Oui, j'ai compris que pourtant (si j'ose dire), les objets pouvaient être utilisés comme des structures, du genre :


     


    " view.bounds = CGRectMake(0, 0, 100,50); "  //ceci, je ne l'ai jamais fait sous MacOS

     

    Donc, sans re-écrire toutes les méthodes, il doit y avoir une solution "stable" de remplacement.

     

    Je regrette néanmoins, j'ai toujours apprécié "avant" la stabilité des structures C et leur lisibilité dans le code.

     

    Par ailleurs, j'ai besoin de mettre de nombreux tableaux de float ou de int. Le compilateur rejette :


    @property(assign) float valeursOnde[24];


    J'en ai pourtant besoin..


     


    Sans parler du fait que j'emploie des structures imbriquées, avec des tableaux de la structure A dans la structure B...


  • HerveHerve Membre

    Le problème vient peut-être d'ailleurs, car j'utilise aussi des structures C dans les classes graphiques, et là  leur comportment est stable.


     


    C'est avec la classe audio que j'ai ce problème...


  • AliGatorAliGator Membre, Modérateur
    Bah rien ne t'empêche de continuer à  utiliser des stuctures C, mais tu vas devoir te palucher la gestion mémoire de ces structures à  la main du coup.
  • HerveHerve Membre

    Aucun problème par rapport à  cela, c'est ce que j'ai toujours fait. Mais "malloc" ne semble pas suffire... J'ai essayé aussi "calloc", pas mieux. 


     


    C'est étonnant parce que tout CoreAudio fonctionne avec des structures C!! Bon, on voit demain...


  • CéroceCéroce Membre, Modérateur
    mai 2014 modifié #8

    D'après la doc, cela a l'air "normal" : il semble qu'il soit déconseillé d'utiliser des structures C sous iOS avec ARC.



    Alors, soyons clair: ARC gère la mémoire des objets Cocoa; c'est à  dire que tu n'as plus à  te palucher toi-même les retain, release et autorelease. Mais c'est tout. ARC ne gère pas la mémoire des structures ni des objets Core Foundation.


    Maintenant, s'il est déconseillé d'utiliser des structures, c'est parce qu'elles deviennent du coup moins pratiques que des classes, puisque justement tu vas devoir gérer leur allocation et leur libération à  la main. Ceci dit, les structures restent un outil judicieux quand les performances sont en jeu. Crois-moi, CGRect n'est pas prêt de disparaà®tre !

     



    Jusqu'à  présent, sous MacOS, je n'utilisais pas ARC. Les structures avaient un comportement très stable. Avec ARC, elles "fuient" à  peine initialisées.


    Je fais la méthode 



    monInstance = malloc(sizeof(maStructureC));

    dans la méthode init. Pas de problème tant que j'utilise cette structure dans "init". Mais dès que j'appelle cette structure dans une autre méthode, elle a disparu...

     



     


    Je le répète, ARC ne gère pas la mémoire des structures, mais la mémoire des objets Cocoa. Si ton objet est codé comme ça:

     



    @interface StructWrapper:NSObject
    {
    MaStruct *structPtr;
    }

    @end

    @implementation StructWrapper

    - (instanceType) init
    {
    self = [super init];
    if(self)
    {
    structPtr = malloc(sizeof(MaStruct));
    }
    return self;
    }

    - (void) dealloc
    {
    free(structPtr);
    }

    @end


    Alors c'est parfait. La structure sera conservée en mémoire tant que l'objet sera conservé en mémoire. Maintenant, si tu t'amuses à  passer la structure à  un autre objet et que l'objet est libéré, alors forcément, la structure sera également libérée et tu auras un plantage.


  • HerveHerve Membre

    Merci Céroce, cela me rassure. Je ne vois pas où j'ai pu libérer la mémoire, d'autant plus que c'est dès la méthode d'initialisation  que le problème se pose.


    Est-ce que le fait que la classe concernée soit une classe partagée ("shareInstance") que le problème se pose? Sans-doute dois-je utiliser la méthode "+ (instancetype) sharedManager" pour la méthode init?


  • CéroceCéroce Membre, Modérateur
    Sans code, nous ne pouvons pas te dire où se trouve l'erreur.
  • HerveHerve Membre
    mai 2014 modifié #11

    Alors va pour une partie du code, le reste est pareil :


    Les structures :



    //voix de synthèse
    struct MesParametresNotes{
    float phase;
    float phase2;
    int statutNote; //0 pour noteOn, 1 pour release, 2 pour noteOff
    float frequency;
    int numNote;

    float velMIDIval;

    float stepVal[24];
    int positionP;//etc.

    //Pan et Delay
    float spaceBox[5000];

    };
    typedef struct MesParametresNotes MesParametresNotes;

    //données globales du synthétiseur
    struct MesParametresSon{
    float volume;
    int noteEnCours;
    float tempoVal;

    //pour Pad
    float ctX;
    float ctY;
    float lastCtX;
    float lastCtY;

    float modMIDIVal;

    MesParametresNotes mesNotes[12];
    UneCouleurSon globalColor;

    };
    typedef struct MesParametresSon MesParametresSon;

    le "+instanceType" :



    + (instancetype) sharedManager
    {
    static AudioManager* sharedInstance;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
    // dispatch_once permet de n'exécuter du code qu'une seule et unique fois dans la vie de l'appli
    // Donc on passera ici la toute première fois que cette méthode est appellée, mais pas les fois suivantes
    sharedInstance = [[AudioManager alloc] init];
    });
    return sharedInstance;
    }

    L'init : (- (id) init{})



    self = [super init];
    if (self) {

    mesParametresSon = malloc(sizeof(mesParametresSon));

    mesParametresSon->volume = 0.8;
    mesParametresSon->tempoVal = 120.0;
    mesParametresSon->ctX = 0.5;
    mesParametresSon->ctY = 0.5;
    mesParametresSon->lastCtX = 0.5;
    mesParametresSon->lastCtY = 0.5;
    mesParametresSon->noteEnCours = 0;
    //etc.
    for (int i = 0; i < 12; i ++){
    mesParametresSon->mesNotes[i].statutNote = 2;
    mesParametresSon->mesNotes[i].frequency = 0.062689;
    mesParametresSon->mesNotes[i].velMIDIval = 0.0;

    mesParametresSon->mesNotes[i].positionP = 0;

    mesParametresSon->mesNotes[i].phase = 0.0;
    mesParametresSon->mesNotes[i].phase2 = 0.0;

    for (int p = 0; p < 5000; p++)mesParametresSon->mesNotes[i].spaceBox[p] = 0.0;

    }//fin boucles i voix du synthé
    //etc.

  • CéroceCéroce Membre, Modérateur
    mai 2014 modifié #12
    J'ai un doute... que se passe-t-il si tu déclares
     
    static AudioManager* sharedInstance;
    En haut de ton fichier .m ?
  • HerveHerve Membre
    mai 2014 modifié #13

    Merci Céroce. Que veux-tu dire exactement?


    En dessous de "@implementation AudioManager", cela ne donne rien de mieux, et bien sûr on ne peut le mettre dans le header. Sinon il est dans la méthode "+ (instancetype) sharedManager.(voir plus haut)


     


    Ceci dit, le AUGraph audio lui aussi a tendance à  "fuiter", donc le problème semble bien venir de l'instanciation de la classe AudioManager



    @interface AudioManager : NSObject{
    AUGraph _graph; //CoreAudio
    AUNode _synthNode; //CoreAudio
    AUNode _mixerNode; //CoreAudio
    AUNode _outputNode; //CoreAudio

    PatchDazibao *monPatch; //structureC
    ProgrammesMIDI *mesProgrammes; //structureC
    AjoutOnde *ajoutOnde; //structureC
    MesParametresSon *mesParametresSon; //structureC

    int largeurCadre;

    int afltModSel, avibModSel, adetModSel, aspcModSel;
    float afltModVal, avibModVal, adetModVal, aspcModVal;

    int nbSteps;
    float doubleTempo;
    }

    Avec les @property pour les types premiers.


  • AliGatorAliGator Membre, Modérateur
    Juste une question, pourquoi dans tes variables d'instance tu utilises un pointeur vers une structure MesParametresSon (MesParametresSon* mesParametresSon), qui t'oblige du coup à  faire un malloc dessus pour la remplir, plutôt que d'utiliser directement "MesParametresSon mesParametresSon" et pouvoir te débarasser du malloc dans le init?

    Bon y'a peut-être une raison pour laquelle tu préfères un pointeur de struct que la struct direct, et ça explique pas le problème que tu as si tu nous dis que ton *mesParametresSon est NULL après le init, mais bon.
  • HerveHerve Membre
    mai 2014 modifié #15

    Bonne remarque AliGator. Bien souvent, dans les structures imbriquées, j'emploie des tableaux de structures (comme "MesParametresNotes mesNotes[12];" dans "struct MesParametresSon". En fait, je me sers des structures comme de classes sans méthode!


     


    Je trouvais  l'écriture plus simple que l'appel de tableaux en Objectiv-C :



    def->mesParametresSon->mesNotes[nt].velMIDIval = 120.0;

    au lieu de 



    [[[def mesParametresSons]mesNotes objectAtIndex:nt]setVelMIDIval:120.0];

    Ceci dit, le problème n'est apparemment pas là . C'est triste car c'est le moteur audio!


     


    Sinon je fais "à  l'ancienne", je l'instancie dans un UIViewController ou l'AppDelegate et je le passe aux autres classes graphiques. Mais comment fait-on alors? Pas moyen de faire des UIViewController les IBOutlet les uns des autres, et AppManager n'apparaà®t pas pas dans les story board d'IB?


     


    Remarque :


    Dans le AppDelegate, et bien que j'appelle à  chaque fois dans le code [AudioManager sharedManager], je dois bien faire :



    - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
    {
    // Override point for customization after application launch.
    _audioManager = [[AudioManager alloc]init];

    return YES;
    }


    ??


  • HerveHerve Membre

    AppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];

    permet d'appeler des instances de l'AppDelegate depuis les UIViewController. J'ai essayé, mais la fuite de mémoire demeure, dans le AU graph au moins. (la communication semblait passer depuis les interfaces graphiques testées vers l'audio manager; j'ai copié via des "@property" l'instance de l'AppDelegate vers les classes du GUI).


  • HerveHerve Membre
    mai 2014 modifié #17

    Je vous embête encore avec mon problème :


    J'ai toujours cette erreur :



    malloc: *** error for object 0x91b5a34: incorrect checksum for freed object - object was probably modified after being freed.
    *** set a breakpoint in malloc_error_break to debug

    tout en ayant changé la création de l'audioManager (désormais créé avec un classique alloc-init et passé de classes en classes avec [[UIApplication sharedApplication] delegate]; ).


     


    En fait, à  la première utilisation la classe est stable parfois (j'ai vérifié avec des NSLog, il s'agit bien de la même instance), tandis que lorsque je relance un nouveau test, alors cela plante. Il me faut redémarrer le Mac pour que cela cesse de planter... au mieux une seule fois!


     


    Surtout, j'ai mis un message console (NSLog) sur la méthode "dealloc" de la classe, je ne le vois jamais apparaà®tre en console. Aussi le problème vient sans doute de là  (j'ai tendance à  arrêter la simulation avec le bouton "stop" de XCode plutôt que depuis le simulateur.)


     


     


    (Cela faisait la même chose avec "shareInstance", mais il semble que le comportement de la classe concernée soit tout de même plus stable maintenant.)


     


     


    Quelqu'un aurait une idée? Un synthé avec une classe audio qui plante, je ne le sens pas...


    Merci par avance.


  • CéroceCéroce Membre, Modérateur
    À vrai dire, je ne comprends pas pourquoi une shared instance ne fonctionnerait pas. Mais je ne comprends pas si c'est clair pour toi: jamais tu ne feras d'[[alloc] init] sur cette classe, tu utiliseras toujours la méthode -[sharedManager].
  • Merci Céroce. J'ai commencé à  supprimer une des structures en en faisant une classe. Ce qui est étonnant est que l'autre structure fonctionne parfaitement! 


     


    Je me demande si le problème ne venait pas des tableaux de flottants imbriqués dans ces structures (les bugs semblaient venir de là )



    struct A (
    float desNombres[5000];
    }

    structB{
    A a[12];
    }

    L'initialisation de "desNombres" posait problème.


     


    A moins que cela ne vienne du fait que j'avais plusieurs jeux de structures imbriquées? Mais je ne pense pas. C'est bien mes tableaux dans mes tableaux dans... qui buggaient. (les structures restantes sont aussi imbriqués les unes dans les autres, mais sans tableaux de types premiers)


     


    Les premiers tests avec la nouvelle classe sont bons, il n'y a plus qu'à  réécrire beaucoup du code pour MacOS...


  • Cela remarche. J'ai donc fait des classes pour supprimer les structures contenant les tableaux de float.


    C'est "résolu", mais je suis surpris que la structure initiale n'ait pas marché... (que ce soit avec une "shared instance", ou avec une instance copiée de classe à  classe).



    //for i de 0 à  11...

    for (int p = 0; p < 5000; p++)mesParametresSon->mesNotes[i].spaceBox[p] = 0.0;
    for (int p = 0; p < 500; p++)mesParametresSon->mesNotes[i].spaceBox2[p] = 0.0;

    Ce sont ces deux lignes qui faisaient apparemment que la structure était désallouée en mémoire. Dans le contrôle de l'utilisation de la mémoire de XCode, on voyait un pic soudain lors du lancement qui disparaissait tout aussi soudainement.


  • AliGatorAliGator Membre, Modérateur
    juin 2014 modifié #21
    Je pense que ton hypothèse est la bonne... des tableaux de tableaux ça doit faire un peu trop d'indirections pour qu'il s'y retrouve et il devait perdre les pédales à  cause de ce double niveau.


    Du moins c'est ce qui me paraà®t le plus plausible.
Connectez-vous ou Inscrivez-vous pour répondre.