Framework ARC + "Vrai" code = Crash ?

Dans mon projet de mise en réseau CoreData je viens de tomber sur un mauvais bug... CocoaHTTPServer, le framework HTTP sur lequel je me suis basé, utilise ARC. Pour pas me faire chier avec les flag compilo et pas avoir à  utiliser ce que je considère être une grosse daube, j'ai embarqué CocoaHTTPServer dans un framwork. ça fonctionne très bien dans son ensemble sauf que je viens de tomber sur un cas foireux...



Dans la méthode de gestion des réponses aux méthodes HTTP je dois renvoyer un objet type HTTPResponse qui contient header et body à  envoyer au client. La méthode que j'implémente ressemble à  ceci :


<br />
- (NSObject&lt;HTTPResponse&gt; *)httpResponseForMethod:(NSString *)method URI:(NSString *)path<br />
{<br />
    __block NSObject&lt;HTTPResponse&gt; * returnHTTPResponse = nil;<br />
   <br />
 &lt;snip&gt;<br />
			   <br />
			    if ([method isEqualToString:@&quot;GET&quot;]) {<br />
				    returnHTTPResponse = [self methodGETWithPath:path andPathCompontents:pathComponents];<br />
			    } else if ([method isEqualToString:@&quot;POST&quot;]) {<br />
	 ORrunOnMainQueueWithoutDeadlocking(^{<br />
	  returnHTTPResponse = [self methodPOSTWithPath:path andPathCompontents:pathComponents];<br />
	 });<br />
			    } else if ([method isEqualToString:@&quot;PUT&quot;]) {<br />
	 ORrunOnMainQueueWithoutDeadlocking(^{<br />
	  returnHTTPResponse = [self methodPUTWithPath:path andPathCompontents:pathComponents];<br />
	 });<br />
			    } else if ([method isEqualToString:@&quot;DELETE&quot;]) {<br />
	 ORrunOnMainQueueWithoutDeadlocking(^{<br />
	  returnHTTPResponse = [self methodDELETEWithPath:path andPathCompontents:pathComponents];<br />
	 });<br />
			    } else {<br />
				    // Unknow method<br />
			    }<br />
			   <br />
		    } <br />
<br />
<br />
 &lt;snip&gt;<br />
<br />
    if (returnHTTPResponse) {<br />
	    return returnHTTPResponse;<br />
    }<br />
   <br />
return [super httpResponseForMethod:method URI:path];<br />
}<br />




En toute logique, returnHTTPResponse est dans le bassin d'autorelease à  la fin de ma méthode.





De l'autre coté de ce code, se trouve le framework CocoaHTTPServer qui fait ceci :


<br />
// Respond properly to HTTP &#39;GET&#39; and &#39;HEAD&#39; commands<br />
httpResponse = [self httpResponseForMethod:method URI:uri];<br />




httpResponse étant une ivar.



Il semblerait que le framwork ne fait pas de retain ici. Plus tard dans l'exécution NSZombie me crie dessus par ce qu'un bout du CocoaHTTPServer fait un release sur un objet déjà  libéré...



À savoir que l'implémentation mon répondeur est une sous classe du système de base fournis par le framework qui ressemble normalement à  ceci :


<br />
- (NSObject&lt;HTTPResponse&gt; *)httpResponseForMethod:(NSString *)method URI:(NSString *)path<br />
{<br />
HTTPLogTrace();<br />
<br />
// Override me to provide custom responses.<br />
<br />
NSString *filePath = [self filePathForURI:path allowDirectory:NO];<br />
<br />
BOOL isDir = NO;<br />
<br />
if (filePath &amp;&amp; [[NSFileManager defaultManager] fileExistsAtPath:filePath isDirectory:&amp;isDir] &amp;&amp; &#33;isDir)<br />
{<br />
  return [[HTTPFileResponse alloc] initWithFilePath:filePath forConnection:self];<br />
<br />
  // Use me instead for asynchronous file IO.<br />
  // Generally better for larger files.<br />
 <br />
// return [[[HTTPAsyncFileResponse alloc] initWithFilePath:filePath forConnection:self] autorelease];<br />
}<br />
<br />
return nil;<br />
}<br />








Est-ce que quelqu'un a une idée sur comment dois-je gérer ma mémoire ? Qu'est-ce que je suis censé renvoyer à  ARC pour qu'il soit content ?

Réponses

  • Puisque ARC ne bosse qu'au moment de la compilation je ne vois pas pourquoi ça poserait problème.. Je n'ai pas encore utilisé assez ARC pour t'aider.. mais à  tout hasard si tu fais un safe-return ça donne quoi?

    Par safe-return, j'entend:




    <br />
    [color=#000088][font=monospace][size=3]if[/size][/font][/color][color=#000000][font=monospace][size=3] [/size][/font][/color][color=#666600][font=monospace][size=3]([/size][/font][/color][color=#000000][font=monospace][size=3]returnHTTPResponse[/size][/font][/color][color=#666600][font=monospace][size=3])[/size][/font][/color][color=#000000][font=monospace][size=3] [/size][/font][/color][color=#666600][font=monospace][size=3]{[/size][/font][/color]<br />
    [color=#000000][font=monospace][size=3]            [/size][/font][/color][color=#000088][font=monospace][size=3]return[/size][/font][/color][color=#000000][font=monospace][size=3] [returnHTTPResponse retain] autorelease][/size][/font][/color][color=#666600][font=monospace][size=3];[/size][/font][/color]<br />
    [color=#000000][font=monospace][size=3]    [/size][/font][/color][color=#666600][font=monospace][size=3]}[/size][/font][/color]<br />
    <br />
    <br />
    <br />
    [color=#666600][font=monospace][size=3]
    [/size][/font][/color]









    Aussi, es-tu sur qu'il n'y aurait pas une de ces méthodes qui ne retournerait pas ton objet avec un release en trop?


    <br />
    [color=#000000][font=monospace][size=3] [/size][/font][/color][color=#000088][font=monospace][size=3]if[/size][/font][/color][color=#000000][font=monospace][size=3] [/size][/font][/color][color=#666600][font=monospace][size=3]([[/size][/font][/color][color=#000000][font=monospace][size=3]method isEqualToString[/size][/font][/color][color=#666600][font=monospace][size=3]:@[/size][/font][/color][color=#008800][font=monospace][size=3]&quot;GET&quot;[/size][/font][/color][color=#666600][font=monospace][size=3]])[/size][/font][/color][color=#000000][font=monospace][size=3] [/size][/font][/color][color=#666600][font=monospace][size=3]{[/size][/font][/color]<br />
    [color=#000000][font=monospace][size=3]                                    returnHTTPResponse [/size][/font][/color][color=#666600][font=monospace][size=3]=[/size][/font][/color][color=#000000][font=monospace][size=3] [/size][/font][/color][color=#666600][font=monospace][size=3][[/size][/font][/color][color=#000088][font=monospace][size=3]self[/size][/font][/color][color=#000000][font=monospace][size=3] methodGETWithPath[/size][/font][/color][color=#666600][font=monospace][size=3]:[/size][/font][/color][color=#000000][font=monospace][size=3]path andPathCompontents[/size][/font][/color][color=#666600][font=monospace][size=3]:[/size][/font][/color][color=#000000][font=monospace][size=3]pathComponents[/size][/font][/color][color=#666600][font=monospace][size=3]];[/size][/font][/color]<br />
    [color=#000000][font=monospace][size=3]                            [/size][/font][/color][color=#666600][font=monospace][size=3]}[/size][/font][/color][color=#000000][font=monospace][size=3] [/size][/font][/color][color=#000088][font=monospace][size=3]else[/size][/font][/color][color=#000000][font=monospace][size=3] [/size][/font][/color][color=#000088][font=monospace][size=3]if[/size][/font][/color][color=#000000][font=monospace][size=3] [/size][/font][/color][color=#666600][font=monospace][size=3]([[/size][/font][/color][color=#000000][font=monospace][size=3]method isEqualToString[/size][/font][/color][color=#666600][font=monospace][size=3]:@[/size][/font][/color][color=#008800][font=monospace][size=3]&quot;POST&quot;[/size][/font][/color][color=#666600][font=monospace][size=3]])[/size][/font][/color][color=#000000][font=monospace][size=3] [/size][/font][/color][color=#666600][font=monospace][size=3]{[/size][/font][/color]<br />
    [color=#000000][font=monospace][size=3]         [/size][/font][/color][color=#660066][font=monospace][size=3]ORrunOnMainQueueWithoutDeadlocking[/size][/font][/color][color=#666600][font=monospace][size=3](^{[/size][/font][/color]<br />
    [color=#000000][font=monospace][size=3]          returnHTTPResponse [/size][/font][/color][color=#666600][font=monospace][size=3]=[/size][/font][/color][color=#000000][font=monospace][size=3] [/size][/font][/color][color=#666600][font=monospace][size=3][[/size][/font][/color][color=#000088][font=monospace][size=3]self[/size][/font][/color][color=#000000][font=monospace][size=3] methodPOSTWithPath[/size][/font][/color][color=#666600][font=monospace][size=3]:[/size][/font][/color][color=#000000][font=monospace][size=3]path andPathCompontents[/size][/font][/color][color=#666600][font=monospace][size=3]:[/size][/font][/color][color=#000000][font=monospace][size=3]pathComponents[/size][/font][/color][color=#666600][font=monospace][size=3]];[/size][/font][/color]<br />
    [color=#000000][font=monospace][size=3]         [/size][/font][/color][color=#666600][font=monospace][size=3]});[/size][/font][/color]<br />
    [color=#000000][font=monospace][size=3]                            [/size][/font][/color][color=#666600][font=monospace][size=3]}[/size][/font][/color][color=#000000][font=monospace][size=3] [/size][/font][/color][color=#000088][font=monospace][size=3]else[/size][/font][/color][color=#000000][font=monospace][size=3] [/size][/font][/color][color=#000088][font=monospace][size=3]if[/size][/font][/color][color=#000000][font=monospace][size=3] [/size][/font][/color][color=#666600][font=monospace][size=3]([[/size][/font][/color][color=#000000][font=monospace][size=3]method isEqualToString[/size][/font][/color][color=#666600][font=monospace][size=3]:@[/size][/font][/color][color=#008800][font=monospace][size=3]&quot;PUT&quot;[/size][/font][/color][color=#666600][font=monospace][size=3]])[/size][/font][/color][color=#000000][font=monospace][size=3] [/size][/font][/color][color=#666600][font=monospace][size=3]{[/size][/font][/color]<br />
    [color=#000000][font=monospace][size=3]         [/size][/font][/color][color=#660066][font=monospace][size=3]ORrunOnMainQueueWithoutDeadlocking[/size][/font][/color][color=#666600][font=monospace][size=3](^{[/size][/font][/color]<br />
    [color=#000000][font=monospace][size=3]          returnHTTPResponse [/size][/font][/color][color=#666600][font=monospace][size=3]=[/size][/font][/color][color=#000000][font=monospace][size=3] [/size][/font][/color][color=#666600][font=monospace][size=3][[/size][/font][/color][color=#000088][font=monospace][size=3]self[/size][/font][/color][color=#000000][font=monospace][size=3] methodPUTWithPath[/size][/font][/color][color=#666600][font=monospace][size=3]:[/size][/font][/color][color=#000000][font=monospace][size=3]path andPathCompontents[/size][/font][/color][color=#666600][font=monospace][size=3]:[/size][/font][/color][color=#000000][font=monospace][size=3]pathComponents[/size][/font][/color][color=#666600][font=monospace][size=3]];[/size][/font][/color]<br />
    [color=#000000][font=monospace][size=3]         [/size][/font][/color][color=#666600][font=monospace][size=3]});[/size][/font][/color]<br />
    [color=#000000][font=monospace][size=3]                            [/size][/font][/color][color=#666600][font=monospace][size=3]}[/size][/font][/color][color=#000000][font=monospace][size=3] [/size][/font][/color][color=#000088][font=monospace][size=3]else[/size][/font][/color][color=#000000][font=monospace][size=3] [/size][/font][/color][color=#000088][font=monospace][size=3]if[/size][/font][/color][color=#000000][font=monospace][size=3] [/size][/font][/color][color=#666600][font=monospace][size=3]([[/size][/font][/color][color=#000000][font=monospace][size=3]method isEqualToString[/size][/font][/color][color=#666600][font=monospace][size=3]:@[/size][/font][/color][color=#008800][font=monospace][size=3]&quot;DELETE&quot;[/size][/font][/color][color=#666600][font=monospace][size=3]])[/size][/font][/color][color=#000000][font=monospace][size=3] [/size][/font][/color][color=#666600][font=monospace][size=3]{[/size][/font][/color]<br />
    [color=#000000][font=monospace][size=3]         [/size][/font][/color][color=#660066][font=monospace][size=3]ORrunOnMainQueueWithoutDeadlocking[/size][/font][/color][color=#666600][font=monospace][size=3](^{[/size][/font][/color]<br />
    [color=#000000][font=monospace][size=3]          returnHTTPResponse [/size][/font][/color][color=#666600][font=monospace][size=3]=[/size][/font][/color][color=#000000][font=monospace][size=3] [/size][/font][/color][color=#666600][font=monospace][size=3][[/size][/font][/color][color=#000088][font=monospace][size=3]self[/size][/font][/color][color=#000000][font=monospace][size=3] methodDELETEWithPath[/size][/font][/color][color=#666600][font=monospace][size=3]:[/size][/font][/color][color=#000000][font=monospace][size=3]path andPathCompontents[/size][/font][/color][color=#666600][font=monospace][size=3]:[/size][/font][/color][color=#000000][font=monospace][size=3]pathComponents[/size][/font][/color][color=#666600][font=monospace][size=3]];[/size][/font][/color]<br />
    [color=#000000][font=monospace][size=3]         [/size][/font][/color][color=#666600][font=monospace][size=3]});[/size][/font][/color]<br />
    [color=#000000][font=monospace][size=3]                            [/size][/font][/color][color=#666600][font=monospace][size=3]}[/size][/font][/color][color=#000000][font=monospace][size=3] [/size][/font][/color][color=#000088][font=monospace][size=3]else[/size][/font][/color][color=#000000][font=monospace][size=3] [/size][/font][/color][color=#666600][font=monospace][size=3]{[/size][/font][/color]<br />
    [color=#000000][font=monospace][size=3]                                    [/size][/font][/color][color=#880000][font=monospace][size=3]// Unknow method[/size][/font][/color]<br />
    [color=#000000][font=monospace][size=3]                            [/size][/font][/color][color=#666600][font=monospace][size=3]}[/size][/font][/color]<br />
    [color=#000000][font=monospace][size=3]                           <br />
                        [/size][/font][/color][color=#666600][font=monospace][size=3]}[/size][/font][/color][color=#000000][font=monospace][size=3] [/size][/font][/color]<br />
  • yoannyoann Membre
    Le truc avec les erreur d'autorelease c'est que c'est la merde à  reproduire... En l'occurrence sans modification je n'arrive plus à  le reproduire...



    Concernant mes méthodes, j'en suis certain d'autant que je ne travaillais que sur une seule.



    Bref, actuellement ça fonctionne mais je suis pas du tout confiant sur la partie mémoire...
  • FKDEVFKDEV Membre
    Normalement, il faut voir du côté des mots clé __bridge et __bridge_transfer pour indiquer à  ARC s'il doit prendre le propriété de la variable ou ne rien faire.
  • yoannyoann Membre
    Merci, c'est l'info que je cherchais.
  • zoczoc Membre
    mai 2012 modifié #6
    'FKDEV' a écrit:
    Normalement, il faut voir du côté des mots clé __bridge et __bridge_transfer pour indiquer à  ARC s'il doit prendre le propriété de la variable ou ne rien faire.
    __bridge_retained et __bridge_transfer sont utilisés uniquement en relation avec le "Toll Free bridging" entre objets Cocoa et CoreFoundation.
    • __bridge_transfer (ou la macro équivalente CFBridgingRelease(i)) doit être utilisé quand on cast un objet CoreFoundation en son équivalent Cocoa (on indique au compilateur qu'on transfère la propriété de l'objet CF vers ARC. ARC se chargera de faire un release sur l'objet Cocoa).
    • __bridge_retained (ou la macro équivalente CFBridgingRetain(i)) doit être utilisé quand on cast un objet Cocoa en son équivalent CoreFoundation (on indique au compilateur que l'on cast un objet retenu par ARC, qui en perd la propriété. Le développeur devra appeler CFRelease sur l'objet CF obtenu).
    • __bridge doit être utilisé pour tout cast entre un objet Cocoa et un pointeur non typé (par exemple void *, on indique au compilateur un transfert de propriété sans changement du retain count), et inversement.


    Le compilateur est capable de se rendre compte tout seul que ces casts manquent et balance une jolie erreur. De plus, ces mots clés ne sont fonctionnels que quand on compile avec ARC activé... Si j'ai bien compris, yoann utilise une librairie tierce utilisant ARC, mais son code ne l'utilise pas. Il n'a donc pas de raison d'utiliser ces mots clés dans son code.
  • Heu ouais ça m'étonnait aussi, me semblait que c'était réservé pour faire du bridging CF/Cocoa... Aucun rapport avec le cas de Yoann donc
  • FKDEVFKDEV Membre
    mai 2012 modifié #8
    'zoc' a écrit:


    __bridge_retained et __bridge_transfer sont utilisés uniquement en relation avec le "Toll Free bridging" entre objets Cocoa et CoreFoundation.




    Non pas uniquement . ARC est plus général, c'est une fonctionnalité d'un compilateur objective-C. Cocoa et CF sont des libraries (resp. Objective C et C).

    Bon j'admets que je ne connais pas beaucoup d'autres librairie C qui renvoie des types opaques qui sont "reference counted".


    'zoc' a écrit:
    [*]__bridge doit être utilisé pour tout cast entre un objet Cocoa et un pointeur non typé (par exemple void *, on indique au compilateur un transfert de propriété sans changement du retain count), et inversement.




    ça, c'est juste faux.

    Un exemple :
    <br />
    	CFStringRef valueCF = CFPreferencesCopyAppValue(CFSTR(&quot;someKey&quot;), CFSTR(&quot;com.company.someapp&quot;));<br />
    	NSString *value = (__bridge NSString *)valueCF;<br />
    	CFRelease(valueCF);<br />
    	[self useValue: value];<br />




    Vu ici : http://www.mikeash.c...-counting.html

    (D'autre part tu me donneras d'autres exemples de pointeur non typé que void *.) image/wink.png' class='bbc_emoticon' alt=';)' />


    'zoc' a écrit:


    Si j'ai bien compris, yoann utilise une librairie tierce utilisant ARC, mais son code ne l'utilise pas. Il n'a donc pas de raison d'utiliser ces mots clés dans son code.




    J'ai jamais dit qu'il devait les utiliser dans son code. CocoaHTTPServer est open source et ce n'est pas initialement un framework il me semble. C'est bien dans le framework qu'il faudrait ajouter les mots clés.



    Du point de vue du framework le code de Yoann est opaque et renvoie des objets qui sont reference counted donc il ne me parait pas complétement idiot d'utiliser les bridged cast. La situation est similaire à  un code ARC utilisant CoreFoundation.

    On est en face d'un problème de transfert de propriété entre ARC et non-ARC et les bridged casts servent justement à  résoudre ces transferts. C'est pour cette raison que j'ai dit de regarder dans cette direction. Je sais bien que je ne m'adresse pas à  un débutant qui va appliquer sans comprendre.





    Cela dit, tes remarques m'ont obligés à  essayer de déchiffrer la doc de ARC, et on y parle de casts entre "retainable object pointer type" et "non-retainable pointer type". Donc le cas de yoann qui se passe entre deux retainable object pointer type ne serait effectivement pas concerné.



    Si le problème était reproductible, je pense que cela vaudrait quand même le coup d'essayer. Ne serait-ce que pour mieux comprendre comment fonctionne ARC.
  • zoczoc Membre
    'FKDEV' a écrit:


    ça, c'est juste faux.

    Un exemple :
    <br />
    	CFStringRef valueCF = CFPreferencesCopyAppValue(CFSTR(&quot;someKey&quot;), CFSTR(&quot;com.company.someapp&quot;));<br />
    	NSString *value = (__bridge NSString *)valueCF;<br />
    	CFRelease(valueCF);<br />
    	[self useValue: value];<br />



    Certes, mais en pratique, sur les plateformes Apple, ce dont je parle depuis le début (je ne l'ai effectivement pas mentionné, mais je ne suis pas là  pour faire de la théorie sur la compilation de code Objective-C en général, et de toute façon, je cherche encore une autre plateforme qui utilise Objective-C, hormis l'anecdotique GNUStep), on n'utilise jamais __bridge dans ce cas, puisque ton exemple est strictement équivalent à :


    <br />
    	CFStringRef valueCF = CFPreferencesCopyAppValue(CFSTR(&quot;someKey&quot;), CFSTR(&quot;com.company.someapp&quot;));<br />
    	NSString *value = (__bridge_transfer NSString *)valueCF;<br />
        [self useValue: value];<br />




    ou encore à 




    <br />
    	CFStringRef valueCF = CFPreferencesCopyAppValue(CFSTR(&quot;someKey&quot;), CFSTR(&quot;com.company.someapp&quot;));<br />
    	NSString *value = (NSString *)CFBridgingRelease(valueCF);<br />
        [self useValue: value];<br />




    et, toujours sur plateformes Apple, __bridge_transfer et __bridge_retained n'ont d'utilité que pour le tollfree bridging entre Cocoa et CF.



    Donc, pour généraliser (mais du coup je suis sûr que c'est moins clair pour le développeur Cocoa moyen):
    • __bridge est utilisé pour convertir un pointeur sur objet "retainable" en un pointeur non "retainable", et inversement, sans transfert de propriété.
    • __bridge_transfer est utilisé pour convertir un pointeur non "retainable" en un pointeur sur objet "retainable" et en transférer la propriété à  ARC.
    • __bridge_retained est utilisé pour convertir un pointeur sur objet "retainable" en un pointeur non "retainable", ARC abandonnant sa propriété.
  • yoannyoann Membre
    Et dans l'histoire je n'arrive toujours pas à  reproduire le problème alors que je n'ai rien changé en terme de gestion mémoire...



    J'aime vraiment, mais alors vraiment pas ARC...
  • zoczoc Membre
    mai 2012 modifié #11
    'yoann' a écrit:
    J'aime vraiment, mais alors vraiment pas ARC...


    Je crois pourtant qu'il va falloir que tu y passes...



    Parce que si mon sentiment était jusqu'à  présent qu'Apple laisserait le choix aux développeurs, la terminologie qu'ils utilisent actuellement ("transition to ARC") me laisse de plus en plus penser qu'à  terme il n'y aura plus de choix possible. Mais ce n'est pas pour tout de suite, car Mountain Lion (et Xcode 4.4 DP4) supporte toujours le code non ARC.



    Et clairement, j'ai beau retourner le problème dans tous les sens, je ne comprends toujours pas les réticences à  utiliser ARC... ARC n'est pas un garbage collector, ARC n'a pas les impacts d'un garbage collector en terme de performances. ARC n'a que des avantages vis à  vis de la gestion de mémoire manuelle ou d'une gestion mémoire par un GC.

  • yoannyoann Membre
    Personnellement, j'ai un background d'électronique, j'aime savoir ce qu'il se passe dans ma machine. Le principe de fonctionnement d'ARC est OK pour moi vu que c'est avant la compilation, par contre je n'apprécie pas de ne pas voir réellement le code mis en place.



    J'aurais préféré qu'Xcode propose de voir en grisé le code généré par ARC histoire de savoir ce qu'il se passe réellement et de ne pas attendre de voir des bug d'exécution pour voir qu'on a oublier d'ajouter certain mot clef ésotérique d'ARC.



    On doit jouer avec un truc qui nous gère la mémoire tout seul mais qui n'affiche pas son travail. J'aime pas du tout...
  • zoczoc Membre
    mai 2012 modifié #13
    'yoann' a écrit:


    voir des bug d'exécution pour voir qu'on a oublier d'ajouter certain mot clef ésotérique d'ARC.


    Tu ne peux pas oublier, puisque tout oubli se solde par une erreur de compilation.



    Et c'est bien un autre avantage de ARC par rapport à  la gestion manuelle: Là  où la gestion manuelle nécessite une réflexion systématique, et où tout oubli se traduit par un plantage ou un leak à  l'exécution, ARC permet de porter son attention sur le code et prévient par une erreur de compilation là  où il n'est pas capable de se débrouiller seul: Si tu fais un cast en oubliant le mot clé approprié, le compilateur ne laissera pas passer...








    'yoann' a écrit:


    On doit jouer avec un truc qui nous gère la mémoire tout seul mais qui n'affiche pas son travail. J'aime pas du tout...


    Ne code jamais en ruby, python ou perl alors. Parce que grosso modo ces langages utilisent un modèle de gestion mémoire proche de ARC. Et ils ont tous prouvé leur efficacité.
  • yoannyoann Membre
    Quand bien même ça donne une erreur de compilation, on ne voit pas ce qu'il fait. Impossible pour le développeur de valider le comportement d'ARC. D'autant que le system de retain / release, désolé mais c'est très simple comme système.



    Savoir ce qu'il se passe en terme de comportement mémoire c'est juste primordial pour optimiser des opérations...
  • AliGatorAliGator Membre, Modérateur
    mai 2012 modifié #15
    @yoann : +10 000



    Je ne dis pas que ARC n'est pas bien, je crois même qu'il est certainement très efficace.



    Mais :

    - Le système de retain/release est simple à  comprendre je trouve aussi comme yoann, chacun est responsable des objets qu'il possède, ça m'a toujours semblé logique

    - Je n'aime pas ne pas savoir ce qui est fait sous le capot en terme de gestion mémoire, surtout sur un programme à  vocation de tourner sur de l'embarqué. C'est plus fort que moi, j'aime pas quand c'est obscur et nébuleux de savoir où sont retenus mes objets, etc*

    - Mais aussi ça n'aide pas les nouveaux venus à  comprendre la logique de fonctionnement. Or pour moi certes c'est bien de faciliter le travail du développeur, mais j'ai toujours peur (craintes un peu confirmées d'ailleurs ces derniers temps) que ça incite encore moins les noobs à  comprendre ce qu'ils font avec leur code, et donc que ça augmente d'une certaine manière le nombre d'applis "code torchon"... et les boulets qui vont croire que coder c'est facile et même pas faire d'architecture mais direct copier/coller des bouts de code entre eux...



    Bref après c'est juste une question de préférence personnelle, ARC est certainement très bien et très efficace, y'a pas de soucis. C'est juste que ça me donne l'impression de perdre le contrôle.





    Les systèmes de diagnostic électroniques sur les voitures modernes où le mécano branche son appareil et regarde sur le moniteur le résultat des sondes, c'est bien, c'est plus simple pour lui, il n'a plus à  se soucier de mettre les mains dans le moteur. Mais le jour où ça déconne, ou les sondes elles-mêmes ne marchent plus... bah le mécano a perdu l'habitude de regarder dans le moteur, voire les nouveaux ne sauront même plus comment faire, et au final s'ils finissent par ne plus que brancher un câble sur leur moniteur sans savoir mettre les mains dans le moteur quand y'a vraiment besoin, je trouve ça plutôt gênant... image/wink.png' class='bbc_emoticon' alt=';)' />
  • zoczoc Membre
    mai 2012 modifié #16
    'yoann' a écrit:


    Savoir ce qu'il se passe en terme de comportement mémoire c'est juste primordial pour optimiser des opérations...


    En ce qui concerne les optimisations, ARC permet justement des optimisations impossibles à  faire avec une gestion de mémoire manuelle (notamment quand le code est 100% ARC, le runtime, par analyse de la pile d'exécution, est capable de ne pas retain+autorelease les objets retournés par les méthodes).



    L'excellent article de Mike Ash (comme tous ses articles d'ailleurs, je suis un grand fan), cité par FKDEV, explique tout cela très clairement.



    Edit: Sinon, vous pouvez toujours aller voir le source du runtime sur opensource.apple.com, c'est vraiment très intéressant et commenté:


    <br />
    [color=#7b737d]<br />
    /*[/color][color=#7b737d]<br />
      Fast handling of returned autoreleased values.[/color][color=#7b737d]<br />
      The caller and callee cooperate to keep the returned object [/color][color=#7b737d]<br />
      out of the autorelease pool.[/color]<br />
    [color=#7b737d]<br />
      Caller:[/color][color=#7b737d]<br />
        ret = callee();[/color][color=#7b737d]<br />
        objc_retainAutoreleasedReturnValue(ret);[/color][color=#7b737d]<br />
        // use ret here[/color]<br />
    [color=#7b737d]<br />
      Callee:[/color][color=#7b737d]<br />
        // compute ret[/color][color=#7b737d]<br />
        [ret retain];[/color][color=#7b737d]<br />
        return objc_autoreleaseReturnValue(ret);[/color]<br />
    [color=#7b737d]<br />
      objc_autoreleaseReturnValue() examines the caller&#39;s instructions following[/color][color=#7b737d]<br />
      the return. If the caller&#39;s instructions immediately call[/color][color=#7b737d]<br />
      objc_autoreleaseReturnValue, then the callee omits the -autorelease and saves[/color][color=#7b737d]<br />
      the result in thread-local storage. If the caller does not look like it[/color][color=#7b737d]<br />
      cooperates, then the callee calls -autorelease as usual.[/color]<br />
    [color=#7b737d]<br />
      objc_autoreleaseReturnValue checks if the returned value is the same as the[/color][color=#7b737d]<br />
      one in thread-local storage. If it is, the value is used directly. If not,[/color][color=#7b737d]<br />
      the value is assumed to be truly autoreleased and is retained again.  In[/color][color=#7b737d]<br />
      either case, the caller now has a retained reference to the value.[/color]<br />
    [color=#7b737d]<br />
      Tagged pointer objects do participate in the fast autorelease scheme, [/color][color=#7b737d]<br />
      because it saves message sends. They are not entered in the autorelease [/color][color=#7b737d]<br />
      pool in the slow case.[/color][color=#7b737d]<br />
    */[/color]<br />
  • AliGatorAliGator Membre, Modérateur
    mai 2012 modifié #17
    En fait il y a un autre petit détail qui me gêne dans ARC, c'est les spécificités, genre le risque qu'on se dire "bon c'est bon de toute façon il fait tout tout seul, donc on regarde plus rien du tout concernant la gestion mémoire on oublie complètement".

    Sauf que du coup on passe à  côté de risques, par exemple le cas des zeroing weak references avec du multithreading dont Mike Ash parle justement.



    C'est un des cas où justement il faut faire attention et savoir ce qu'on fait et ce que ARC fait sous le capot pour penser à  transformer la weak reference en strong reference localement pour rester thread-safe... (et où le compilateur ne nous dira rien si on ne le fait pas... et en plus les erreurs comme ça risquant des bugs en cas de race-conditions sont particulièrement difficiles à  tracker, justement à  cause du race-condition qui fait que ça n'arrive pas à  tous les coups selon le scheduling des divers threads)



    Bref, certes c'est un cas peu commun (mais justement raison de plus pour qu'on risque de passer à  côté ne le rencontrant pas souvent), mais ce que je veux dire par là  c'est que ce n'est pas parce qu'on utilise ARC qu'il faut totalement arrêter de réfléchir à  comment est géré la mémoire... or pourtant vu ce qu'ARC fait tout seul sans nous dire, ça aurait plutôt tendance à  nous faire tout oublier...
  • FKDEVFKDEV Membre
    mai 2012 modifié #18
    Globalement, j'aime bien ARC. Ca me fait gagner du temps en codage et en debug.

    J'aime bien la sémantique explicite de __strong et __weak.

    En plus je ne savais même pas que __weak remettait à  nil les pointeurs automatiquement.



    Maintenant, dans des cas particuliers, il y a peut-être moyen de forcer une gestion manuelle en utilisant des types opaques persos et des bridge casts, non ?

    C'est juste une intuition, hein, je n'ai pas le temps ni le courage de me pencher là -dessus. Mais si c'est possible quelqu'un comme mikeash nous sortira un article.
  • zoczoc Membre
    'AliGator' a écrit:


    ce n'est pas parce qu'on utilise ARC qu'il faut totalement arrêter de réfléchir à  comment est géré la mémoire...


    Sur ce point je suis entièrement d'accord. Mais dans la majorité des cas, la gestion mémoire manuelle, c'est du systématique. Le systématique, autant le faire faire à  une machine, tu risques moins l'erreur d'inattention parce que c'est tellement systématique que ça en devient la routine.



    Mais clairement, pour les cas particuliers, comme les références weak, qu'il est fortement recommandé de transformer en références strong pour ne pas voir son objet disparaitre en plus milieu d'un bout de code qui l'utilise, ou les retain cycles, le compilateur ne va pas remplacer le cerveau (bien qu'il pourrait très bien générer un warning quand on utilise une variable weak pour autre chose qu'une assignation à  une variable strong).
  • zoczoc Membre
    'FKDEV' a écrit:


    En plus je ne savais même pas que __weak remettait à  nil les pointeurs automatiquement.


    Attention, uniquement sous iOS 5 et MacOS X 10.7.



    Sinon, pour les autres plateformes (iOS 4.2 et MacOS X 10.6), il est possible d'utiliser PLWeakCompatibility.
Connectez-vous ou Inscrivez-vous pour répondre.