Crash avec NSLocalizedString

Eric P.Eric P. Membre
20:39 modifié dans API UIKit #1
Bonjour,

J'ai attaqué la localisation d'iPocket Draw et j'ai un crash bizarre avec NSLocalizedString lors de l'affichage d'une UIActionSheet.
Sans les NSLocalizedString, pas de problème, mais avec ça passe une fois ou deux et ensuite crash sur "objc_msgSend".
A d'autres endroits j'ai également des NSLocalizedString qui fonctionnent sans problème.

Ai-je loupé quelque chose ?

Merci

Eric

Réponses

  • Philippe49Philippe49 Membre
    20:39 modifié #2
    Jamais eu cela, et pourtant ....
    Ce n'est pas un manque de retain ?
    Un NSLog sans @ : comme NSLog("identifier"," ");
    Un fichier de localisation absent, tu dois avoir quelque chose comme l'image
  • Eric P.Eric P. Membre
    20:39 modifié #3
    Bon voilà  ce que j'ai trouvé :

    ce code :
    NSString* sheetTitle = NSLocalizedString(@&quot;Sendbymailas&quot;, @&quot;&quot;);<br />lstring = @&quot;Send by mail&quot;;<br />UIActionSheet* actionSheet = [[UIActionSheet alloc]<br />				initWithTitle: sheetTitle<br />				delegate: self<br />				cancelButtonTitle: NSLocalizedString(@&quot;Cancel&quot;, @&quot;&quot;)<br />				destructiveButtonTitle: NULL<br />				otherButtonTitles: NSLocalizedString(@&quot;RealCADD_file&quot;, @&quot;&quot;), <br />				NSLocalizedString(@&quot;DXF_file&quot;, @&quot;&quot;), NULL];<br />[actionSheet showInView: self.view];<br />[sheetTitle release];<br />[actionSheet release];<br />
    


    crash au 2ème ou 3ème appel.

    Si je n'utilise pas le "NSString* sheetTitle" donc en mettant directement le NSLocalizedString(@Sendbymailas, @";") à  sa place à  la ligne initWithTitle:
    je n'ai pas de crash.
    Ce qui est bizarre (je crois) c'est que je n'ai pas le crash si dans le code ci-dessus je supprime la ligne "[sheetTitle release];"

    Enfin le principal est d'avoir une solution qui fonctionne.

    Merci

    Eric
  • AliGatorAliGator Membre, Modérateur
    mai 2009 modifié #4
    Je ne vois ni le mot "alloc", ni "copy" ni "retain" dans le mot "NSLocalizedString"... donc pourquoi faire un "release" sur cette NSString "sheetTitle" ?
  • Philippe49Philippe49 Membre
    mai 2009 modifié #5
    C'est [sheetTitle release] qui doit provoquer l'erreur. Tu fais un release sur une chaà®ne que tu ne peux désallouer car une NSLocalizedString n'est pas créé dynamiquement.

    Dans l'esprit de ton code, il aurait fallu mettre
    NSString* sheetTitle = NSLocalizedString(@Sendbymailas, @";");
    sans release

    ou de manière plus compliquée :
    NSString* sheetTitle =[ [NSString alloc] initWithString:NSLocalizedString(@Sendbymailas, @";")];
    avec un release.
  • Eric P.Eric P. Membre
    20:39 modifié #6
    Merci pour les réponses.

    Ca m'énerve un peu toutes ces complications de release, retain, alloc et autres joyeusetés...
    Pourquoi quand je fais directement NSString* sheetTitle = @xxx;
    et avec le release, cela ne crash pas ?

    Bonne nuit.

    Eric
  • Philippe49Philippe49 Membre
    20:39 modifié #7
    C'est de la cuisine interne. Ce qui est sur c'est qu'on ne fait pas de release sur une chaà®ne constante ou une NSLocalized. On peut penser qu'elles sont traitées différemment, les premières crées à  la compilation, les secondes plutôt au lancement de l'application : Si il y a 50 langues, quel intérêt de stocker en dur les 50 localisations dans le code exécutable ?   
  • AliGatorAliGator Membre, Modérateur
    20:39 modifié #8
    En effet. Sous le capot ce sont 2 choses différentes :

    1) Lorsque tu utilises une constante, genre @xxx, c'est un cas particulier puisque c'est une constante, inclus dans les données du code compilé, en dur. En fait sous le capot ce type de NSString a un retainCount infini, tout comme un singleton d'ailleurs. On ne peut pas "releaser" une constante

    2) Lorsque tu utilises NSLocalizedString(@xx), qui n'est qu'une macro qui fait appel à  la méthode [tt][[NSBundle mainBundle] localizedStringForKey:@xx value:@";" table:nil][/tt] (c'est dit dans la doc, mais sinon un simple pomme+double-clic sur le mot "NSLocalizedString" dans ton code va te mener au #define correspondant sinon).
    Or cette méthode va charger dynamiquement les chaà®nes depuis le bon fichier ".strings", selon le langage adéquat (et dans la table adéquat si elle est spécifiée)... donc sous le capot, y'a lecture du fichier texte, sans doute une mise en cache de la NSString j'imagine je sais pas trop comment...
    Mais tout ce qu'on sait c'est que vu les conventions de nommage Apple, tu n'as pas à  gérer la mémoire de la chaà®ne retournée par cette macro/cet appel. Très certainement est-elle en tout cas autoreleased.



    Couclusion la question n'est pas tant pourquoi ça crash dans un cas et pas dans l'autre, même si, si tu veux tout savoir je viens de te donner la réponse (retainCount infini dans un cas car constante, autoreleased dans l'autre cas). La question c'est plutôt de savoir pourquoi tu mettrais un release ici, alors que tu n'as ni alloc ni copy ni retain correspondant donc pas besoin de release pour le "balancer"...

    La règle est simple, ne faire un release que qd tu as toi mm fait un alloc ou un copy ou un retain... dans les autres cas, tu n'as pas à  faire de release, c'est pas compliqué, faut pas se poser des questions plus loin ^^
  • Philippe49Philippe49 Membre
    20:39 modifié #9
    dans 1241560257:

    Or cette méthode va charger dynamiquement les chaà®nes depuis le bon fichier ".strings", selon le langage adéquat (et dans la table adéquat si elle est spécifiée)... donc sous le capot, y'a lecture du fichier texte, sans doute une mise en cache de la NSString j'imagine je sais pas trop comment...
    ... Très certainement est-elle en tout cas autoreleased.

    Je pencherais plutôt pour une mise en cache, donc pas autoreleased, mais plutôt dans une zone mémoire où le release n'est pas autorisé.
  • AliGatorAliGator Membre, Modérateur
    20:39 modifié #10
    dans 1241560773:

    Je pencherais plutôt pour une mise en cache, donc pas autoreleased, mais plutôt dans une zone mémoire où le release n'est pas autorisé.
    Héhé, je me doutais que tu allais réagir... mais j'ai fait exprès de formuler ça ainsi, pour sous-entendre qu'à  la limite l'un n'empêche pas l'autre ;)

    Pour moi il est mis en cache qqpart dans le mécanisme interne de NSBundle je sais pas trop comment (et à  vrai dire on s'en fout ^^ tout ce qu'on sait c'est qu'on a pas à  gérer sa mémoire et ça devrait nous suffire :P), mais ça n'empêche pas, pourquoi pas, de se dire qu'il est autoreleased :
    - soit parce qu'il est effectivement autoreleased (j'ai vu par exemple des accesseurs qui retournent [tt][[mavaleur retain] autorelease][/tt] au lieu de juste [tt]mavaleur[/tt], ça pourrait être dans ce cas)
    - soit parce qu'en fait ok c'est pas réellement autoreleased, mais on peut considérer ça comme tel de l'extérieur, à  savoir appliquer la règle : "d'abord c'est pas nous qu'on a créé l'objet donc c'est pas nous qu'on a à  le releaser d'abord nan mais".

    En effet on peut imaginer que qui dit "mise en cache" dis "une fois la chaà®ne chargée en mémoire à  priori elle a des chances de restée chargée pdt toute la vie de l'appli... mais bon à  la limite si on a besoin de mémoire car on est un peu court on peut la décharger, et la recharger alors du fichier quand on en aura re-besoin"... et là  ça peut justifier un retain+autorelease au lieu de retourner la valeur direct.

    Mais bon après je suis d'accord avec toi, c'est pas dit que ce soit autorelease en effet. Mais toujours est-il que bon... en fait en réalité pour l'utiliser, on s'en fiche, c'est pas nous qui faisons de alloc/copy/retain donc on a pas à  se soucier de la gestion mémoire, comme quand ce sont des variables autorelease.
  • Philippe49Philippe49 Membre
    20:39 modifié #11
    Tout à  fait
  • Eric P.Eric P. Membre
    20:39 modifié #12
    Bonjour,

    Alors pourquoi avais-je le release ?
    Car le code provient d'un exemple avec la NSString initialisée avec alloc...initwithformat...
    J'ai ce code ailleurs mais pour un second cas je l'ai modifié avec une constante d'abord puis avec NSLocalizedString mais en laissant le release.
    Maintant je dois vérifier tout mon code.
    On prend de bonnes habitudes avec REALbasic...

    Merci

    Eric
  • Philippe49Philippe49 Membre
    20:39 modifié #13
    Je viens d'avoir la même mésaventure avec du code copié-collé de Beginning iPhone Development : le code mis en ligne comporte de tels grigri, et il met souvent plusieurs lignes de code quand une seule serait suffisante. Ayant moi-même écrit des livres, je sais que c'est vraiment pas simple d'éviter ce genre de "coquilles". Souvent à  la relecture, on change pour améliorer, et on ne maitrise pas forcément tous les impacts.

    Conclusion de Tristan Bernard : "Il ne faut avoir confiance qu'en soi-même, et encore, pas beaucoup"
  • schlumschlum Membre
    20:39 modifié #14
    Maintenant, c'est bien beau tout ça, mais ça m'étonne que ce "release", bien qu'inutile provoque un crash...
    Il me semble qu'on peut faire autant de "retain", "release" qu'on veut sur un objet constant, son "retainCount" ne change point.
  • AliGatorAliGator Membre, Modérateur
    20:39 modifié #15
    Ben justement t'as mal lu schlum, relis le post d'Eric et mon post...
    - Ca crash pas quand il utilise une constante, normal car retainCount virtuellement infini et retain/release sont alors des no-op...
    - Ca crash qd il utilise NSLocalizedString, qui charge la chaà®ne dynamiquement et dans ce cas, qu'elle soit mise en cache qqpart ou autoreleased ou quoi, dans tous les cas c'est pas une constante.

    Donc tout est normal.
  • schlumschlum Membre
    20:39 modifié #16
    Ah oui, sur le NSLocalizedString c'est normal...
    Cependant il me semble que c'est l'application qui charge le fichier dans un dictionnaire au lancement, et pas NSLocalizedString qui va le lire dynamiquement (ce qui revient au même pour le release, certes...)

    Et faut faire gaffe parfois avec ce qui semble être dynamique  :P
    NSLog(@&quot;%p %p %p&quot;,@&quot;toto&quot;,[NSString stringWithString:@&quot;toto&quot;],[[NSString alloc] initWithString:@&quot;toto&quot;]);
    


    Je vous laisse deviner ce que ça renvoie  :)
  • Eric P.Eric P. Membre
    20:39 modifié #17
    Bonjour,

    Je suis content que mon erreur vous permette de philosopher ainsi...

    Bon week-end.

    Eric
  • AliGatorAliGator Membre, Modérateur
    20:39 modifié #18
    dans 1241843076:

    Je vous laisse deviner ce que ça renvoie  :)
    Bah j'imagine 3x la même chose... mais en même temps ça se comprend, il me semble que Cocoa pour pas mal de classes internes utilise du CoW (Copy-On-Write), ce qui expliquerait donc ce comportement avec résultat identique. Surtout sur une NSString (non Mutable donc)... Quand on sait en plus que NSString est un class cluster et donc que sous le capot il y a plusieurs classes privées utilitaires partout, et que c'est une des classes les plus utilisées donc sans doute une des plus optimisées aussi...

    (Voilà  pourquoi c'est pas tjs bon de faire des tests et suppositions et d'en tirer des conclusions sur une classe comme NSString qui est un peu particulière, entre class cluster, optimisation, utilisation de constantes via @..., c'est une des classes les plus spécifiques en interne je pense, donc bon, méfiance quant aux conclusions dessus :P)
  • schlumschlum Membre
    20:39 modifié #19
    Oui, c'est vrai qu'il y a pas mal de trucs " bizarres " sur NSString 

    De quoi faire une thèse  :)
Connectez-vous ou Inscrivez-vous pour répondre.