Le point sur ARC

2

Réponses

  • Entre ce qu'Apple annonce et ce qui est vrai... image/biggrin.png' class='bbc_emoticon' alt=':D' /> image/biggrin.png' class='bbc_emoticon' alt=':D' />
  • 'ldesroziers' a écrit:


    Entre ce qu'Apple annonce et ce qui est vrai... image/biggrin.png' class='bbc_emoticon' alt=':D' /> image/biggrin.png' class='bbc_emoticon' alt=':D' />




    Parfois oui, parfois non :-) Comme je l'avais indiqué je n'ai pas fait de tests moi-même. M'enfin ce sont les données fournies lors de la dernière conf. développeur. À voir.
  • AliGatorAliGator Membre, Modérateur
    Moi ce qui me plairait et je pense que je ne suis pas le seul, c'est un mix entre ARC et non-ARC : continuer à  gérer la mémoire moi-même parce que j'aime bien contrôler ce qui se passe et je n'aime pas quand ce qui se passe sur la gestion de mes objets est obscure, mais que le compilateur me dise "attention là  moi j'aurais mis un retain, et là  j'aurais mis un release" (donc juste qu'il me dise ce qu'il aurait fait, sans le faire à  ma place pour autant)
  • C'est vrai que que quitte à  avoir sorti ARC, autant proposer un truc alternatif pour les ronchons comme Ali et moi image/tongue.png' class='bbc_emoticon' alt=':P' />
  • 'AliGator' a écrit:


    Moi ce qui me plairait et je pense que je ne suis pas le seul, c'est un mix entre ARC et non-ARC : continuer à  gérer la mémoire moi-même parce que j'aime bien contrôler ce qui se passe et je n'aime pas quand ce qui se passe sur la gestion de mes objets est obscure, mais que le compilateur me dise "attention là  moi j'aurais mis un retain, et là  j'aurais mis un release" (donc juste qu'il me dise ce qu'il aurait fait, sans le faire à  ma place pour autant)




    +42



    Mais ça c'est Clang Analyzer
  • mars 2012 modifié #37
    'yoann' a écrit:


    +42



    Mais ça c'est Clang Analyzer


    Ouais mais Clang Analyzer il est pas aussi puissant que ARC a l'air de l'être.. image/sad.png' class='bbc_emoticon' alt=':(' />
  • 'ldesroziers' a écrit:


    Ouais mais Clang Analyzer il est pas aussi puissant que ARC a l'air de l'être.. image/sad.png' class='bbc_emoticon' alt=':(' />




    Pourtant ARC est censé être basé sur Clang...
  • muqaddarmuqaddar Administrateur
    Il me semble que Clang n'indique pas un oubli de release sur une ivar par exemple, contrairement à  un oubli de release dans une variable de méthode.
  • TofTof Membre
    mars 2012 modifié #40
    'Kubernan' a écrit:


    Il y a des règles pour la gestion mémoire sans ARC, il y en a aussi avec l'ARC. Faut juste les adopter, comme tu as pu adopter les retain/release.


    ARC serait vraiment intéressant si justement on n'avait pas à  prendre en compte des règles qui lui sont propres. Tel que c'est fait actuellement ça introduit un risque supplémentaire tous en perdant le contrôle de la maniére dont est utilisé la mémoire. Si dans le futur ARC devient vraiment transparent je ferai des tests pour voir si ça vaut la peine de l'utiliser. En attendant je reste avec les retain/release et n'oublions pas les autorelease qui sont fort pratique.
    'Kubernan' a écrit:
    Enfin, je ne vois pas en quoi c'est plus important de ne pas utiliser l'ARC sur iphone... là  ça m'échappe. Au contraire : Apple annonce des performances jusqu'à  2,5 fois plus rapide que les retain/release. C'est plutôt pas mal pour du code sur iPhone.


    Les annonces qu'Apple fait faut pas les prendre pour argent comptant c'est plus pour un effet marketing qu'autre chose. Le mieux est de faire soit même ses tests afin de vérifier si dans son contexte c'est vrai et utile.

    Sur iPhone et iPad il est important que ton programme consomme le moins de mémoire et le moins de CPU possible. On n'est pas sur un PC ou un Mac, on n'a pas les même ressources et surtout on peut pas changer de CPU ou rajouter de la mémoire. Si tu code ton application iPhone de la même maniére que ton application Mac ton programme marchera mais il mettra à  mal l'iPhone (ou l'iPad) de l'utiliseur. En ayant le controle de la mémoire tu peux déjà  réduire la consommation de ton application quand c'est possible, et plus efficacement et plus intélligement qu'un système automatique. Pour le CPU ça se joue plus au niveau de la conception de ton application.

    Malheureusement dans la pratique les applications que j'ai eu en main c'était du tout et n'importe quoi. Genre une application relativement basic qui prenait 40 secondes pour se lancer. Une autre qui consomme en parmanence du CPU alors que c'est pas nécessaire et vide la batterie très rapidement. Une autre encore qui fini par bouffer toute la mémoire disponible par ce que le gas à  fait un couper/coller d'un algorithme déjà  consommateur et en plus il n'a pas su l'intégrer et l'utiliser correctement.

    Les applications semblent être codées à  la va comme je te pousse. L'analyse et l'architecture sont quasi inexistante comme la documentation du code. Et je suis certain que pour aucune des applications que j'ai eu en main ils ont lancé Instrument.

    C'est des applications "cocktail" : prenez un application , mettez y différents trucs dedans, mélangez bien tous ça et envoyez le ensuite sur l'AppStore. Voilà  c'est servi, reste plus qu'à  mettre l'olive image/smile.png' class='bbc_emoticon' alt=':)' />
  • zoczoc Membre
    'yoann' a écrit:


    Pourtant ARC est censé être basé sur Clang...


    Non, pas vraiment. L'analyse statique du code et ARC sont 2 composants indépendants.



    Il y a un excellent article sur ARC sur le site de Steffen Itterheim
  • MalaMala Membre, Modérateur
    mars 2012 modifié #42
    Très intéressant comme article zoc. J'avoue que je suis un peu déçu qu'il faille bidouiller pour garantir la compatibilité ARC/non ARC. Apple aurait pu faire un effort. image/sad.png' class='bbc_emoticon' alt=':(' />
  • AliGatorAliGator Membre, Modérateur
    Petit retour d'expérience :

    - Au taf je viens de créer un nouveau projet Xcode. On a choisi, parce que moi et mon collègue préférons gérer la mémoire nous même (question d'habitude), ne pas utiliser ARC pour le projet construisant l'application

    - Mais ce projet utilise diverses librairies, ou plutôt divers sous-projets dont il dépend, dont on a les sources (par exemple xmppframework, TBXML, AFNetworking...), et évidemment certains utilisent ARC







    Et là , les problèmes commencent :
    • Si on inclus directement les sources des projets tierces utilisés dans le projet de l'appli (= on copie les fichiers .h et .m de TBXML, AFNetworking, etc dans MonAppli.xcodeproj), non seulement c'est assez fouilli (donc on a souhaité éviter), mais ne plus ça veut dire qu'il faut rajouter à  la main, fichier par fichier, le flag "-fobjc-arc" sur tous les fichiers provenant de codes externes concus avec ARC, notre projet MonAppli.xcodeproj n'ayant pas ARC d'activé
    • Si on fait un xcodeproj par projet tierce, ce qu'on a fait, c'est déjà  plus propre, chaque composant étant indépendant, et les dépendances étant en plus gérées simplement par Xcode lui-même.


    Sauf qu'en choisissant cette dernière option, si tout se compile bien, on a un beau crash au runtime avec comme erreur :


    dyld: lazy symbol binding failed: Symbol not found: _objc_retain
    Et pan dans les dents !





    Mais là  où c'est plus sioux encore, c'est que déjà  on ne se rend pas compte à  la compilation qu'il va y avoir un problème (aucun warning, aucune erreur) mais seulement au Runtime... et encore ! Car sous iOS5, ça marche très bien, aucun plantage ! Ce plantage de "symbol _objc_retain not found" n'intervient que si on exécute l'appli sur iOS4.x (testé sur 4.2.1) !





    Après avoir cherché 2h le pourquoi du comment et comment s'en sortir, la solution est toute simple... enfin une fois qu'on la connait !! Parce que c'est pas évident si on ne connait pas le truc !

    Il suffit d'ajouter le flag "-fobjc-arc" à  "Other Linker Flags" de MonAppli.xcodeproj (oui, ce flag à  rajouter pour le linker a le même nom que celui qu'on ajoute aux Compiler Flags) et là  tout rentre dans l'ordre.



    Xcode n'a donc pas réussi à  deviner que, même si MonAppli.xcodeproj n'a pas ARC d'activé, vu que notre projet a des dépendances avec d'autres xcodeproj qui eux utilisent ARC, il fallait qu'il rajoute ce flag. Et si on le sait pas, on peut passer à  côté d'un plantage fatal détectable uniquement au Runtime et uniquement sur iOS4.x et pas iOS5 !







    Alors j'aurais bien aimé transformer ces projets xcodeproj tiers non pas en simple librairie dont notre MonAppli.xcodeproj dépend, mais en "framework statique" (ce qui aurait été encore plus projet et aurait permis de mieux gérer les dépendances en évitant de devoir remonter dans MonAppli les dylib et frameworks dont dépendent ces xcodeproj). Je pense que si on avait transformé tous ces xcodeproj tiers en des frameworks statiques (ce qu'on n'a pas fait par simple manque de temps) ça aurait peut-être amélioré les choses aussi sur ce point de vue là ... et encore j'en suis pas sûr.



    Mais pour une première expérience à  devoir travailler avec des projets utilisant ARC, perdre une bonne partie de l'après-midi à  s'arracher les cheveux pour un plantage de ce genre, ça freine un peu, m'voyez...
  • LeChatNoirLeChatNoir Membre, Modérateur
    Bon, moi j'avoue que j'en reviens aussi...

    J'ai un objet de ma propre invention qui me fait mes download.



    Ca utiliser le principe des delegate.



    Bon, depuis que je suis passé en ARC, cet objet est déclaré en unsafe _unretained et ça semble poser des pb... Ca me provoque des crash quand je quitte une vue alors que le dl est pas encore terminé...



    Alors peut être que c'est mon utilisation du delegate qui n'est pas bonne mais avant que je migre en ARC, je n'avais pas ce pb...



    image/angry.gif' class='bbc_emoticon' alt='>:(' />
  • zoczoc Membre
    'AliGator' a écrit:
    Après avoir cherché 2h le pourquoi du comment et comment s'en sortir, la solution est toute simple... enfin une fois qu'on la connait !! Parce que c'est pas évident si on ne connait pas le truc ! Il suffit d'ajouter le flag "-fobjc-arc" à  "Other Linker Flags" de MonAppli.xcodeproj


    C'est clairement un bug, mais en même temps, chez Apple ils supposent clairement que tout le monde va utiliser ARC pour les nouveaux projets. Donc ils ont bossé à  fond sur l'intégration de code ancien (non ARC) dans du code ARC, et pas l'inverse, ce qui est exactement ce que vous faites...



    Le problème que vous avez eu ne se produit en effet que sous iOS 4 (et MacOS X 10.6), car ce système dispose d'un runtime qui n'est pas "ARC ready", et par conséquent le linker doit rajouter le code nécessaire à  l'exécutable pour qu'il fonctionne sur ce système.
  • zoczoc Membre
    mars 2012 modifié #46
    'LeChatNoir' a écrit:
    cet objet est déclaré en unsafe _unretained


    Et pourquoi pas weak (à  moins que tu cibles un OS qui ne le supporte pas), ça éviterait au moins le problème de crash (mais vu le plantage que tu décris, ton delegate est détruit trop tôt, c'est ce problème qu'il faut régler).



    ARC a tendance à  favoriser les appels à  "release" par rapport à  "autorelease". C'est d'ailleurs clairement expliqué dans la vidéo de la WWDC à  propos de ARC. La plupart du temps il arrive à  optimiser en remplaçant autorelease par release, là  où l'humain ne verra pas forcément une optimisation possible et utilisera autorelease. Conséquence, le code produit avec ARC conduit parfois à  la destruction d'objets plus tôt que le code équivalent produit par un humain. Ca a un effet certain sur les références weak ou unsafe_retained, qui peuvent du coup se retrouver invalide avant le drainage du release pool...



    Au final, là  où du code non ARC pouvait sembler planter aléatoirement, à  cause d'un "timing fluctuant", le code ARC équivalent va planter systématiquement. C'est à  mon avis pas plus mal, au moins ça évite les retours de clients mécontents.
  • juste une petite question.



    si je décide d'utiliser ARC, quel intérêt de continuer à  utiliser la forme [NSString string*] au lieu de [[NSString alloc] init*] puisque je n'ai plus à  m'occuper des retain/release ?
  • Moins de texte.
  • je viens de convertir un 'petit' projet en ARC.



    c'est assez bluffant : même la gestion du NSTimer est correctement convertit (c'est ce que je craignais le plus)



    par contre c'est assez frustrant d'avoir fait l'effort depuis des années pour gérer (et d'abord comprendre) le mécanisme retain/release/autorealease et d'accepter que ARC remet tout à  plat... mais bon pourquoi pas ;-)



    c'est pas la première fois qu'Apple nous fait ce coup là  (et c'est peut être tant mieux) ...

    le progrès est peut être à  ce prix
  • Lorsque l'on fait des choses un peu plus compliquées, avec des blocks, des retains mutuels... c'est toujours bon de connaitre la méthode sans ARC, pour comprendre ce qu'il se passe.
  • AliGatorAliGator Membre, Modérateur
    octobre 2012 modifié #51
    @xyloweb : L'effort que tu as fait de comprendre le fonctionnement de la mémoire n'est pas vain, et est même à  mon avis toujours aussi nécessaire. Rien que pour comprendre le problème des Retain-Cycle, qu'on peut d'ailleurs toujours avoir avec des property strong même avec ARC (un des exemples les plus courants étant un block retenu par un objet et dont le corps de block utilise une référence à  "self")



    C'est même d'ailleurs ce qui me fait un peu "peur" avec l'avènement d'ARC, c'est que les nouveaux venus qui vont faire des projets en ayant connu qu'ARC ne vont pas faire l'effort du coup de comprendre le principe "d'ownership" qui est le coeur de la gestion mémoire (qui est responsable de tel ou tel objet, qui a un lien fort (strong/retain) ou faible (weak, assign) avec tel ou tell objet.



    C'est pas parce qu'on utilise ARC qu'il n'est pas nécessaire de comprendre les mécanismes de gestion mémoire, en particulier le principe d'ownership des objets, au contraire. Les principes d'ownership sont les mêmes, c'est juste qu'ils ne nous servent plus pour dire "faut un retain ou un release ici" mais pour dire "il faut un strong ici et un weak ici".



    Sans ces bases de compréhension de la mémoire, on risque fort de faire par exemple des retain-cycle et des objets qui ne se détruisent jamais, et ça ARC n'y changera rien en cas de retain-cycle.



    Bon plus ça va plus les nouvelles versions de LLVM et de Clang nous préviennent à  coup de warning en cas de retain-cycle, mais ça n'empêche pas d'aider à  comprendre.
  • muqaddarmuqaddar Administrateur
    octobre 2012 modifié #52
    Quelle est la meilleure façon d'implémenter un singleton avec ARC ?



    J'ai vu ça:


    + (MyClass *)sharedInstance<br />
    {<br />
    	static MyClass *sharedInstance = nil;<br />
    	static dispatch_once_t onceToken;<br />
    	dispatch_once(&amp;onceToken, ^{<br />
    		sharedInstance = [[MyClass alloc] init];<br />
    		// Do any other initialisation stuff here<br />
    	});<br />
    	return sharedInstance;<br />
    }




    Dash propose la même chose en exemple:


    <br />
    #import &quot;__class__.h&quot;<br />
    <br />
    @implementation __class__<br />
    <br />
    + (__class__ *)__accessor__<br />
    {<br />
    	static dispatch_once_t once;<br />
    	static __class__ *__singleton__;<br />
    	dispatch_once(&amp;once, ^ { __singleton__ = [[__class__ alloc] init]; });<br />
    	return __singleton__;<br />
    }<br />
    <br />
    @cursor<br />
    <br />
    @end<br />




    Merci de vos retours.
  • KubernanKubernan Membre
    octobre 2012 modifié #53
    'muqaddar' a écrit:


    Quelle est la meilleure façon d'implémenter un singleton avec ARC ?



    J'ai vu ça:



    Merci de vos retours.




    C'est la façon dont j'implémente mes singletons depuis iOS 5. Le dispatch_once m'est utile également depuis qu'iOS 6 a introduit - (BOOL)application:(UIApplication *)application willFinishLaunchingWithOptions:(NSDictionary *)launchOptions



    Mes procédures d'init sont appelées dans didFinishLaunching... et willFinishLaunching... via une méthode que nous appelleront commonInit.

    Tout le code à  l'intérieur de commonInit est encadré dans un dispatch_once. Si l'application est exécutée sous iOS 6 les deux méthodes willFinishLaunching... et didFinishLaunching... appelleront successivement commonInit mais le code de cette méthode ne sera donc exécuté qu'une seule fois. Sous iOS 5 seul didFinishLaunching étant appelé, ça marche aussi. Pratique.
  • <br />
    + (MyClass *)sharedInstance<br />
    {<br />
    	    static MyClass *sharedInstance = nil;<br />
    	    if ( &#33; sharedInstance )<br />
    			    sharedInstance = [[MyClass alloc] init];<br />
    	    return sharedInstance;<br />
    }<br />




    Je me demande ce qui est plus rapide. Ce if testé à  chaque fois ou le dispatch_once.
  • KubernanKubernan Membre
    octobre 2012 modifié #55
    'Thibaut' a écrit:

    <br />
    + (MyClass *)sharedInstance<br />
    {<br />
    		static MyClass *sharedInstance = nil;<br />
    		if ( &#33; sharedInstance )<br />
    				sharedInstance = [[MyClass alloc] init];<br />
    		return sharedInstance;<br />
    }<br />




    Je me demande ce qui est plus rapide. Ce if testé à  chaque fois ou le dispatch_once.




    Le disptach_once ne t'assure pas seulement que ton code est exécuté qu'une fois, il est également thread safe :

    If called simultaneously from multiple threads, this function waits synchronously until the block has completed.



    Du coup me suis pas posé la question en terme de rapidité : j'ai un singleton spécialement dédié pour tous les éléments de mon interface, appelé chaque fois que j'ai une couleur, un png à  charger pour un bouton ou autre etc... je n'ai vraiment aucun problème de perf.
  • AliGatorAliGator Membre, Modérateur
    octobre 2012 modifié #56
    Le dispatch_once, définitivement le dispatch_once. Sans aucune hésitation.



    Le if (!sharedInstance) n'assure pas la thread-safety, comme indiqué par Kubernan.



    Le dispatch_once de plus d'être thread-safe, est optimisé un max comme le sont toutes les méthodes de GCD (j'irais presque jusqu'à  dire que quand on voit du GCD faut même plus hésiter à  l'utiliser ^^). En pratique, il utilise en interne un double-check lock, ce qui permet d'être thread-safe tout en évitant l'overhead d'un mutex kernel (qui serait lent sinon), et qui revient dans 95% des cas (les cas où il n'y a pas de race-condition entre 2 threads) à  un truc aussi rapide qu'un simple "if" (et les 5% restants nécessite un lock kernel... mais parce qu'il y a une race-condition et que le mutex est donc nécessaire pour éviter les crash pour cause de multi-threading)





    Par contre dans le code qu'a cité muqaddar, je ne suis pas d'accord avec le
    // Do any other initialisation stuff here


    Car en pratique le pattern dont on parle ici n'est pas un vrai singleton (mais plutôt le pattern qu'on va appeler SharedInstance à  défaut de me rappeler si c'est le vrai nom officiel), qui permet d'avoir accès à  une instance partagée, lazy-loaded, mais vivante pendant toute la durée de l'application une fois chargée (et en pratique, c'est ce qu'on veut quand on utilise ce pattern), mais qui n'empêche pas la création d'autres instances, indépendantes de la sharedInstance.



    Bien sûr en pratique quand on met en place ce pattern pour une classe, dans le reste de l'appli on n'utilise la classe qu'à  travers sa méthode +sharedInstance et en général on s'arrange pour ne jamais appeler alloc/init dessus nous-même. Mais rien n'empêche qu'on puisse le faire, pour une raison X ou Y. Et dans ce cas tout ce qu'on initialise habituellement pour notre singleton doit l'être aussi pour nos autres instances.



    Donc si on veut être rigoureux, j'écrirais plutôt (au passage j'utilise le mot clé instancetype histoire en plus de permettre le subclassing, car pourquoi l'interdire?) :
    +(instancetype)sharedInstance<br />
    {<br />
      static MyClass* sharedInstance = nil;<br />
      static dispatch_once_t onceToken;<br />
      dispatch_once(&amp;onceToken,^{<br />
    	sharedInstance = [[MyClass alloc] init];<br />
    	// Do any initialization only specific to the sharedInstance here, generally nothing<br />
      });<br />
      return sharedInstance;<br />
    }<br />
    <br />
    -(id)init<br />
    {<br />
       self = [super init];<br />
       if (self) {<br />
    	 // Do any initialization specific for your class here<br />
       }<br />
       return self;<br />
    }
    En pratique, je préfère en général mettre les trucs que j'ai besoin d'initialiser dans le init. Que ce soit un singleton ou pas comme ça on a le même type de code. Et puis en plus comme ça on peut imaginer partir d'une classe non-singleton, à  usage général, qui a déjà  son init et tout le reste. Et un jour on décide de lui rajouter une sharedInstace, il suffit de rajouter le code GCD sans rien toucher au reste et ça continuera de marcher.
  • MalaMala Membre, Modérateur
    Question peut être stupide mais un simple "@synchronized(self)"; à  la place du "dispatch_once" cela ne fonctionne plus avec ARC?
  • AliGatorAliGator Membre, Modérateur
    octobre 2012 modifié #58
    Pour les intéressés, un exemple pratique de la différence entre les 2 placements du code d'init



    Imaginez que vous créez une classe TVService qui va vous permettre de taper dans un WebService qui liste disons les programmes TV des chaà®nes de la TNT. Vous allez faire une classe avec :
    • une propriété baseURL qui indique le point d'entrée de votre WebService,
    • des méthodes pour demander la liste des chaà®nes (qui va vous retourner leur ID, leur nom, et tout ça), la liste des programmes d'une chaà®ne à  une heure donnée, etc, et tout ce qu'il vous faut.
    • Vous aurez peut-être besoin d'une propriété disons de type NSCache (ou d'un NSDictionary mais NSCache c'est mieux image/tongue.png' class='bbc_emoticon' alt=':P' />) pour mettre en cache la liste des chaà®nes par exemple et pas la redemander à  chaque fois.


    Dans le -init de ce service, vous allez donc écrire :
    -(id)init {<br />
      self = [super init];<br />
      if (self) {<br />
    	_programListCache = [[NSCache alloc] init];<br />
      }<br />
      return self;<br />
    }
    Ou un truc comme ça, pour initialiser votre instance de NSCache.

    Par contre, dans un premier temps, votre classe TVService étant générique et devant pouvoir se connecter à  n'importe quel service TV qui suit l'API que vous avez implémenté, à  chaque instance de définir la valeur de baseURL à  utiliser comme entry point.



    ---



    Puis un jour vous allez avoir un fournisseur de WebService préféré, et allez vous dire que ce serait bien d'avoir une sharedInstance accessible dans toute l'appli. Du coup c'est le moment d'écrire le code GCD de la méthode sharedInstance... et d'initialiser cette instance particulière avec la bonne valeur pour la propriété baseURL, valeur à  n'affecter qu'à  la sharedInstance (et pas aux autres potentielles instances)


    +(instancetype)sharedInstance<br />
    {<br />
      static TVService* sharedInstance = nil;<br />
      static dispatch_once_t onceToken;<br />
      dispatch_once(&amp;onceToken,^{<br />
            sharedInstance = [[TVService alloc] init];<br />
            sharedInstance.baseURL = kYourDefaultWebServiceProvider;<br />
      });<br />
      return sharedInstance;<br />
    }
    Il y a bien là  une différence entre les 2 endroits où on fait des initialisations : l'une est générique à  toutes les instances (dans init), l'autre propre à  notre sharedInstance (donc dans son dispatch_once après son propre init).



    Dans cet exemple, même si on a une sharedInstance qu'on a prévu pour aller taper sur notre fournisseur de WS préféré / dédiée, rien ne nous empêche de créer dans notre appli d'autres instances de TVService qui ne sont pas la sharedInstance (et c'est tant mieux), et de leur affecter leur propre baseURL différente de celle de la sharedInstance, juste parce que ponctuellement on a besoin d'utiliser un autre fournisseur de WS utilisant la même API (un miroir par exemple, etc) plutôt que d'utiliser la sharedInstance commune à  toute l'appli.







    Tout ça pour dire que même si dans 99% des cas on utilise ce code de sharedInstance comme un singleton, maintenant il ne faut plus le voir comme un vrai singleton au sens strict (interdisant la possibilité de créer de nouvelles instances autres que la sharedInstance), mais comme une classe qui nous offre juste l'accès à  une instance accessible de partout dans l'application (mais n'interdisant pas de créer d'autres instances indépendantes si vraiment le besoin s'en fait sentir).

    D'ailleurs c'est mieux, car c'est un des reproches qu'on fait souvent au pattern Singleton, bien souvent il n'y a aucune justification à  réellement interdire la création de nouvelles instances, le besoin principal étant juste d'avoir accès à  une instance commune depuis partout dans l'appli, de façon thread-safe et propre.
  • AliGatorAliGator Membre, Modérateur
    octobre 2012 modifié #59
    'Mala' a écrit:


    Question peut être stupide mais un simple "@synchronized(self)"; à  la place du "dispatch_once" cela ne fonctionne plus avec ARC?
    Je pense que si, il n'y a pas de raison que @synchronized(self) ne marche plus, c'est juste moins performant qu'un dispatch_once (car @synchronized n'est pas double-check lock si je ne m'abuse)



    Disons qu'il y a plusieurs types de locks/mutex disponibles en Objective-C, chacun ayant leurs avantages et inconvénients (et c'est un très vaste sujet). Certains locks sont exception-ready (c'est le cas des portions entourées de @synchronized qui verrouillent le mutex à  leur entrée et le déverrouillent à  la sortie de la section... que ce soit une sortie "normale" ou suite à  une exception dans la section encadrée de la directive), certains sont réentrants, certains permettent la lecture multi-thead mais l'écriture mono-thread (pour ne pas bloquer inutilement les threads si tous ne demandent qu'une lecture des données sans écriture)... chacune a ses + et - et ses coûts en terme de performances.
  • CeetixCeetix Membre
    octobre 2012 modifié #60
    Ali, j'ai lu ton post sur le singleton 2.0 et je suis bien d'accord avec l'évolution que tu donnes à  cette structure mais dans ton exemple, comme fais-tu pour récupérer une instance qui t'intéresse ?

    J'ai peut être la mauvaise idée de créer un dictionnaire qui stockera toutes mes instances et je piocherai dedans en fonction d'un identifiant mais sinon je vois pas trop comment faire autrement.
  • muqaddarmuqaddar Administrateur
    octobre 2012 modifié #61
    Wouah.

    C'est devenu chaud pendant la nuit...



    J'ai bien compris avec l'exemple de TVService ! image/implore.gif' class='bbc_emoticon' alt=' o:) ' />



    Sinon, juste pour info, voilà  comment j'écris mon singleton sans ARC:


    [color=#78492A][font=Monaco][size=2]#pragma mark-[/size][/font][/color]<br />
    [color=#78492A][font=Monaco][size=2]#pragma mark singleton[/size][/font][/color]<br />
    <br />
    [font=Monaco][size=2]+ ([color=#bb2ca2]void[/color])initialize [/size][/font]<br />
    [font=Monaco][size=2]{[/size][/font]<br />
    [color=#4F8187][font=Monaco][size=2][color=#bb2ca2]if[/color][color=#000000] (&#33;[/color]sharedInstance[color=#000000]) [[[/color][color=#bb2ca2]self[/color][color=#000000] [/color][color=#3d1d81]alloc[/color][color=#000000]] [/color][color=#3d1d81]init[/color][color=#000000]];[/color][/size][/font][/color]<br />
    [font=Monaco][size=2]}[/size][/font]<br />
    <br />
    [font=Monaco][size=2]+ ([color=#bb2ca2]id[/color])sharedManager[/size][/font]<br />
    [font=Monaco][size=2]{[/size][/font]<br />
    [color=#4F8187][font=Monaco][size=2][color=#bb2ca2]return[/color][color=#000000] [/color]sharedInstance[color=#000000];[/color][/size][/font][/color]<br />
    [font=Monaco][size=2]}[/size][/font]<br />
    <br />
    [font=Monaco][size=2]+ ([color=#bb2ca2]id[/color])allocWithZone:([color=#703daa]NSZone[/color] *)zone [/size][/font]<br />
    [font=Monaco][size=2]{[/size][/font]<br />
    [color=#4F8187][font=Monaco][size=2][color=#bb2ca2]if[/color][color=#000000] ([/color]sharedInstance[color=#000000]) [/color][color=#bb2ca2]return[/color][color=#000000] [[/color]sharedInstance[color=#000000] [/color][color=#3d1d81]retain[/color][color=#000000]];[/color][/size][/font][/color]<br />
    [color=#BB2CA2][font=Monaco][size=2][color=#000000]  [/color]else[color=#000000] [/color]return[color=#000000] [[/color]super[color=#000000] [/color][color=#3d1d81]allocWithZone[/color][color=#000000]:zone];[/color][/size][/font][/color]<br />
    [font=Monaco][size=2]}[/size][/font]<br />
    <br />
    [font=Monaco][size=2]- ([color=#bb2ca2]id[/color])init [/size][/font]<br />
    [font=Monaco][size=2]{[/size][/font]<br />
    [color=#4F8187][font=Monaco][size=2][color=#bb2ca2]  if[/color][color=#000000] (&#33;[/color]sharedInstance[color=#000000]) [/color][/size][/font][/color]<br />
    [font=Monaco][size=2]  {[/size][/font]<br />
    [font=Monaco][size=2]	[color=#bb2ca2]self[/color] = [[color=#bb2ca2]super[/color] [color=#3d1d81]init[/color]];[/size][/font]<br />
    [font=Monaco][size=2][color=#bb2ca2]	if[/color] ([color=#bb2ca2]self[/color]) [/size][/font]<br />
    [font=Monaco][size=2]	{[/size][/font]<br />
    [color=#008400][font=Monaco][size=2]	  // set ivars[/size][/font][/color]<br />
    [font=Monaco][size=2]	} [/size][/font]<br />
    [color=#4F8187][font=Monaco][size=2]	sharedInstance[color=#000000] = [/color][color=#bb2ca2]self[/color][color=#000000];[/color][/size][/font][/color]<br />
    [font=Monaco][size=2]  } [/size][/font]<br />
    [color=#4F8187][font=Monaco][size=2][color=#000000]  [/color][color=#bb2ca2]else[/color][color=#000000] [/color][color=#bb2ca2]if[/color][color=#000000] ([/color][color=#bb2ca2]self[/color][color=#000000] &#33;= [/color]sharedInstance[color=#000000]) [/color][/size][/font][/color]<br />
    [font=Monaco][size=2]  {[/size][/font]<br />
    [color=#3D1D81][font=Monaco][size=2][color=#000000]	[[/color][color=#bb2ca2]self[/color][color=#000000] [/color]release[color=#000000]];[/color][/size][/font][/color]<br />
    [color=#4F8187][font=Monaco][size=2][color=#bb2ca2]	self[/color][color=#000000] = [/color]sharedInstance[color=#000000];[/color][/size][/font][/color]<br />
    [font=Monaco][size=2]  } [/size][/font]<br />
    [color=#BB2CA2][font=Monaco][size=2]  return[color=#000000] [/color]self[color=#000000];[/color][/size][/font][/color]<br />
    [font=Monaco][size=2]}
    [/size][/font]
Connectez-vous ou Inscrivez-vous pour répondre.