Discussion à  propos de l'Objective-C 2

psychoh13psychoh13 Mothership DeveloperMembre
03:53 modifié dans Actualités #1
Bon il est temps de mettre mon grain de sel dans cette discussion et aussi de la relancer sous l'impulsion de Schlum.
à‰tant donné que Leopard sort dans 7 jours, je pense qu'on peut mettre de côté la NCA et dévoiler quelques pans des nouvelles technologies de développement d'Apple.

J'aimerais vous apporter quelques éclaircissement au sujet du GC et des propriétés dans Objective-C 2.0 :

Pour commencer, le GC, celui-ci fonctionne grâce à  des "objets racines", tout objet référencé par un objet racine ou par un objet référencé par l'objet racine n'est pas collecté (supprimé). En revanche, s'il est impossible d'accéder à  l'objet grâce à  partir d'un objet racine, l'objet est collecté, ceci permet d'éviter que des références circulaires empêchent la suppression des objets.

Pour finir, les propriétés. Il s'agit d'un système pour simplifier l'appel aux accesseurs et modificateurs, et cela ne concerne à  priori que les accesseurs et modificateurs, cependant on peut "pervertir" les propriétés afin de les utiliser dans un autre but que l'accès aux variables d'instance.
Car il faut bien comprendre que les propriétés ne sont pas un rejet de l'encapsulation mais plutôt une simplification d'écriture (sucre syntaxique si vous voulez :D), les propriétés ne correspondent donc pas à  un accès direct aux variables d'instance, mais à  un envoie de message, vous n'aurez donc aucun gain de vitesse avec ce système, seulement un gain de lisibilité, car l'affectation ressemblera à  n'importe quel affectation de variable :

monObject.nom = @"un nom";


Et l'accès se fera de la même façon :

NSLog(@"Mon nom est %@", monObject.nom);


Comment ça fonctionne dans l'implémentation ? Et bien vous définissez une propriété grâce à  la directive @property, de la manière suivante :

@property(attributes) type name;


Parmi les attributs, on citer en vrac "readonly", "retained", "copy", etc.
ça c'est la déclaration. Dans l'implémentation vous avez le choix, soit vous laissez le compilateur s'en occuper, il générera les méthodes lui-même en rapport avec les attributs que vous lui avez indiqué. Ou alors vous lui fournissez les méthodes que vous voulez qu'il utilise. Cela permet donc de manière simple d'utiliser sous le capots des méthodes éventuellement assez complexes.


Personnellement, l'utilisation des propriétés ne me donne pas plus envie de vénérer que de crier au scandal. Ayant connu les propriétés sous C# (c'est de la qu'elles viennent) j'ai pu voir qu'on peut faire des manipulations très fines sans pour autant utiliser une syntaxe rébarbative (genre être obligé de mettre des parenthèses vides pour un getter...), la possibilité nous est donné de pouvoir définir des propriétés sans qu'il y ait une variable de classe derrière on peut donc imaginer toutes sortes de choses.
En ce qui concerne la syntaxe : objet.propriété, l'utilisation du point est peut-être plus claire, car il fait la séparation entre l'accès aux variables d'instance publiques pour lesquelles ont utilise l'opérateur "->" et l'utilisation des propriétés.
En effet, le '.' signifie ici qu'on utilise la référence de l'objet et pas l'objet lui-même, l'opérateur "->" signifie qu'on accède directement au contenu de l'objet en le déréférençant.

Donc pour résumer : les propriétés ont les mêmes avantages et inconvénients que les méthodes, leur résolution est dynamique, le compilateur peut vérifier l'existence d'une propriété, etc. elles ont en revanche deux avantages : la syntaxe plus simple (plus claire pour certains, plus obscure pour d'autres), et l'auto-génération du code par le compilateur.

Personnellement, les nouveautés d'Objective-C qui me bottent sont l'énumération rapide, les propriétés et les protocoles partiels, pour ce dernier, l'option qui me bottait le plus manque toujours à  l'appel, c'est-à -dire la possibilité d'écrire l'implémentation "par défaut" des méthodes optionnelles du protocole.
Ce système aurait permis de définir une implémentation des méthodes déclarées comme @optional dans le protocole, cela aurait permis une plus grande flexibilité : la classe qui se conforme au protocole est assurée que toutes les méthodes optionnelles qu'elle n'implémente pas le sont et donc qu'on peut les utiliser, en revanche si l'utilisateur du protocole pense qu'il est possible de donner une meilleure implémentation, il lui suffit de réimplémenter la méthode. Pour l'instant je n'ai toujours pas vu ce système réintégré dans Objective-C 2.0, j'espère que je le retrouverai dans 7 jours. :(

Bon pour finir, je reviens donc sur l'allocation, le GC dans Objective-C 2.0 ne me botte pas plus que ça, c'est plus une opération marketing pour attirer les Java-oliques et C#-oliques dans le giron d'Objective-C, mais ce système n'empêche pas de faire attention, il se peut que les objets soient supprimés alors qu'on bosse dessus simplement parce qu'on a pas fait attention au fait qu'il doit être référencé par un objet racine.

Sinon, je trouve le système des autorelease pools et de retain/release géniaux, mais je pense aussi que les deux systèmes sont à  utiliser avec attention. Je rejette l'affirmation qui consiste à  dire que les autorelease pools sont à  éviter au maximum, au contraire !
Non seulement pour les arguments déjà  donnés. Mais, par exemple, utiliser une méthode de convenance pour créer des objets destinés à  être stockés dans un NSArray par exemple, ça ne pose aucun problème. Personnellement, je préfère ce système lorsque l'objet ne sort pas d'une méthode, ou alors je fais un simple -retain sur l'objet si je ne veux pas le perdre.

Dans tous les cas, il y a une chose qu'on ne doit pas oublier, et je pense que vous serez tous d'accord avec moi. Quelque soit le système qu'on souhaite employer, si on ne connaà®t les tenants et aboutissants, autant marcher à  l'aveuglette au bord d'un falaise...
L'éducation des programmeurs y est pour beaucoup dans cette histoire, ce qui manque surtout à  mon avis, c'est le fait d'apprendre à  apprendre aux futurs programmeurs et aussi leur donner l'envie d'apprendre par eux-mêmes. Parce que bloquer des élèves dans une technologie parce que le marché l'exige n'est pas bon pour la suite surtout si le marché change radicalement (ce qui arrive souvent en informatique).

PS : Si vous avez survécu à  mon message jusqu'ici je vous félicite ! :D
«1

Réponses

  • Philippe49Philippe49 Membre
    03:53 modifié #2
    dans 1192836001:

    PS : Si vous avez survécu à  mon message jusqu'ici je vous félicite ! :D


    on a survécu , et merci de ces précisions 

    dans 1192836001:

    En ce qui concerne la syntaxe : objet.propriété, l'utilisation du point est peut-être plus claire, car il fait la séparation entre l'accès aux variables d'instance publiques pour lesquelles ont utilise l'opérateur "->" et l'utilisation des propriétés.
    En effet, le '.' signifie ici qu'on utilise la référence de l'objet et pas l'objet lui-même, l'opérateur "->" signifie qu'on accède directement au contenu de l'objet en le déréférençant.


    Qu'entend-tu par "en le déférençant" ?
    La flèche ici risque-t-elle de faire ici confusion avec son utilisation en C ?
  • psychoh13psychoh13 Mothership Developer Membre
    03:53 modifié #3
    On sait que tout objet en Objective-C est un pointeur sur une structure C, c'est pour ça qu'on a systématiquement l'étoile lorsqu'on définit un type :

    NSString *maChaine;<br />// ou<br />- (NSString *)maChaine;
    


    À l'exception de id qui est déjà  un pointeur. Définit comme ça pour ceux qui ne savent pas :
    typedef struct objc_object {<br />    Class isa;<br />} *id;
    


    "isa" étant le pointeur sur la structure de la classe de l'objet que tout objet doit avoir pour pouvoir être reconnu à  l'exécution.

    Bon étant donné que nous n'avons que des pointeur sur objet en Objective-C, si on utilise la flèche comme ça :

    monObjet-&gt;ivarPublique
    


    On accède directement à  la variable d'instance publique (si on est à  l'extérieur de la classe), la flèche étant une simplification de :

    (*monObjet).ivarPublique
    


    On peut donc dire qu'on déréférence l'objet (c'est-à -dire qu'au lieu de manipuler son adresse on y accède directement). Donc utiliser cette syntaxe pour être "cohérent" avec le C, c'est-à -dire déréférencement d'un pointeur puis accès à  la variable, peut-être perturbant, parce que là  on pourrait croire qu'il s'agit d'un membre de la structure, ce qui n'est pas le cas, aucune propriété n'est définie dans la zone mémoire de chaque objet, pas plus qu'une méthode.
    La syntaxe avec le "." sur des pointeurs permet donc de lever l'ambiguà¯té et pour le compilateur et pour le lecteur du code. En effet, en syntaxe C, le "." signifie qu'on accède aux champs d'une structure (non plus d'un pointeur sur une structure), on peut donc lire l'utilisation de ce "." comme un accès aux "membres" de la référence pas de l'objet lui-même, il y a donc là  il y a plus une idée de passer (selon moi) par l'objet classe pour découvrir à  quoi correspond la propriété.

    Personnellement, donc, je pense que l'utilisation de la flèche "->" aurait porté à  confusion, de la même manière que le receveur d'un message est toujours un pointeur (on ne met jamais l'étoile devant "monObjet" qu'on écrit : [monObjet maChaine]; ), l'accès à  une propriété d'un objet ne se fait pas par l'objet lui-même mais par sa référence.
  • Philippe49Philippe49 Membre
    03:53 modifié #4
    Je suis un peu lent,  ::) et il n'y rien de tel que de reformuler par soi-même .  B)
    Merci d'avance pour ta patience. ::)

    dans 1192865359:

    On sait que tout objet en Objective-C est un pointeur sur une structure C, c'est pour ça qu'on a systématiquement l'étoile lorsqu'on définit un type .


    De ce fait,  le "." n'agit pas à  la base sur un objet C, mais seulement la flèche "->" .

    On peut donc penser que la flèche reste ce qu'elle a toujours été en C, c'est-à -dire le contenu d'une variable de la structure, contenu qui peut être-elle même une adresse, un objet, ou un type simple.
    Il peut y avoir quelques enrichissements néanmoins.

    dans 1192865359:

    <br />@property(attributes) type name;<br />
    

    Parmi les attributs, on citer en vrac "readonly", "retained", "copy", etc.
    ça c'est la déclaration. Dans l'implémentation vous avez le choix, soit vous laissez le compilateur s'en occuper, il générera les méthodes lui-même en rapport avec les attributs que vous lui avez indiqué. Ou alors vous lui fournissez les méthodes que vous voulez qu'il utilise. Cela permet donc de manière simple d'utiliser sous le capots des méthodes éventuellement assez complexes.

    Et que Objective-C-2.0 ajoute une fonctionnalité par le "." qui correspond au moins à  ce que l'on connaà®t déjà  dans la syntaxe des bindings, dans le KVC, setValue: forKey: etc...
    La déclaration dans le .h par @property devient alors une sorte d'officialisation, peut-être enrichie, de ce que l'on a déjà , avec l'asssurance d'une implémentation standard.


    dans 1192865359:

    On peut donc dire qu'on déréférence l'objet (c'est-à -dire qu'au lieu de manipuler son adresse on y accède directement)

    Déréferencer est un mot que j'ai du mal à  faire passer aux étudiants dans le cours (élémentaire) sur le C.
    Je préfère parler en termes d'adresse d'une variable, ou du contenu d'une variable, contenu qui peut être lui-même une adresse.
    Enrichissant ensuite cela par le vocabulaire des pointeurs, qui sont eux-mêmes des variables ayant un contenu et une adresse ...
    On a alors en tête un modèle "physique" de ce qui peut se passer sous le capot.
    Je sais que le mot "dereferencing" est utilisé, mais cela me semble une abstraction inutile, sauf à  la traduire mentalement peu ou prou par un modèle comme je l'ai fait ci-dessus. 


  • psychoh13psychoh13 Mothership Developer Membre
    octobre 2007 modifié #5
    dans 1192873657:

    Je suis un peu lent,  ::) et il n'y rien de tel que de reformuler par soi-même .  B)
    Merci d'avance pour ta patience. ::)


    De rien :D Comme je le dis souvent (surtout quand je dis quelque chose de méchant, ce qui n'est pas le cas ici :D) c'est gratuit et offert avec amour. :D

    dans 1192873657:
    De ce fait,  le "." n'agit pas à  la base sur un objet C, mais seulement la flèche "->" .

    On peut donc penser que la flèche reste ce qu'elle a toujours été en C, c'est-à -dire le contenu d'une variable de la structure, contenu qui peut être-elle même une adresse, un objet, ou un type simple.
    Il peut y avoir quelques enrichissements néanmoins.


    Je suis tatillon oui, mais c'est juste pour préciser, la flèche "->" c'est uniquement dans le cas d'un pointeur sur une structure, pour une variable de type structure c'est bien sûr le point "." à  la base.

    dans 1192873657:
    Et que Objective-C-2.0 ajoute une fonctionnalité par le "." qui correspond au moins à  ce que l'on connaà®t déjà  dans la syntaxe des bindings, dans le KVC, setValue: forKey: etc...
    La déclaration dans le .h par @property devient alors une sorte d'officialisation, peut-être enrichie, de ce que l'on a déjà , avec l'asssurance d'une implémentation standard.

    Ce n'est pas vraiment une officialisation des bindings. Car toute propriété ne représente pas forcément une variable d'instance, pas plus que toute variable d'instance n'est forcément représentée par une propriété. Il s'agit purement et simplement d'une simplification de la syntaxe des accesseurs/modificateurs.
    L'utilisation d'une propriété peut remplacer l'appel d'une méthode sans argument (retournant éventuellement quelque chose) OU (exclusif) une méthode ne prenant qu'un seul argument.

    On peut imaginer par exemple dans le cas d'une NSView, l'utilisation d'une propriété NeedsDisplay de cette manière :

    myView.NeedsDisplay = YES;<br />// en lieu et place de :<br />[myView setNeedsDisplay: YES];<br />// ou bien :<br />if(myView.NeedsDisplay) // du code<br />// en lieu et place de :<br />if([myView needsDisplay]) // du code
    


    En revanche, je n'ai pas vu dans la documentation de mention à  propos de ceci :

    BOOL willDisplay = myView.NeedsDisplay = YES;
    


    En C#, dans ce genre de cas, il se passe deux chose, on fait d'abord appel au setter définit pour NeedsDisplay et ensuite on fait appel à  son getter pour mettre la valeur dans BOOL willDisplay. Connaissant le système de messages d'Objective-C, je ne sais pas trop s'ils ont prévu quelque chose sur ce point.

    [edit] Après un petit test, ce comportement est défini et produit donc l'effet désiré, c'est-à -dire que le setter est appelé, suivit du getter. Autre chose, les variables doivent être statiquement typé pour pouvoir répondre à  une propriété, le compilateur émet une erreur si on essaye d'accéder à  une propriété sur une variable de type "id" ou bien sur une classe qui ne l'implémente pas.

    Enfin, toujours est-il que l'utilisation d'une propriété est strictement équivalent à  l'appel d'un message, et donc si une propriété n'est pas définie par le développeur pour une variable d'instance, elle n'existera pas. Alors que dans le cas des bindings, que les accesseurs/modificateurs soient définis ou pas, il est possible d'accéder/modifier leur valeur.


    dans 1192873657:
    Déréferencer est un mot que j'ai du mal à  faire passer aux étudiants dans le cours (élémentaire) sur le C.
    Je préfère parler en termes d'adresse d'une variable, ou du contenu d'une variable, contenu qui peut être lui-même une adresse.
    Enrichissant ensuite cela par le vocabulaire des pointeurs, qui sont eux-mêmes des variables ayant un contenu et une adresse ...
    On a alors en tête un modèle "physique" de ce qui peut se passer sous le capot.
    Je sais que le mot "dereferencing" est utilisé, mais cela me semble une abstraction inutile, sauf à  la traduire mentalement peu ou prou par un modèle comme je l'ai fait ci-dessus.


    ça je suis d'accord, "déréférencer" est un gros mot pour les profanes et même pas toujours facile à  comprendre pour ceux qui sont pourtant dans le milieu. Il est malheureusement bien ancré dans mon esprit pour décrire le fait de manipuler l'objet plutôt que de manipuler son adresse.
    Il représente bien pour moi ce système :

    int var = 5<br />int *ptr = &amp;var;<br /><br />ptr: |_|----&gt;|5|
    


    Désolé pour le dessin ascii pas forcément très clair :D (les barres parallèles représentant une case mémoire :D)
  • schlumschlum Membre
    03:53 modifié #6
    dans 1192836001:
    Pour commencer, le GC, celui-ci fonctionne grâce à  des "objets racines", tout objet référencé par un objet racine ou par un objet référencé par l'objet racine n'est pas collecté (supprimé). En revanche, s'il est impossible d'accéder à  l'objet grâce à  partir d'un objet racine, l'objet est collecté, ceci permet d'éviter que des références circulaires empêchent la suppression des objets.


    Mhhh, pas très sexy  :-\\ Ca veut dire que si on veut garder un objet, il ne suffit plus de faire un "retain" dessus, il faut en plus faire gaffe à  ce qu'il soit relié par un objet racine. Bof...

    Donc pour résumer : les propriétés ont les mêmes avantages et inconvénients que les méthodes, leur résolution est dynamique, le compilateur peut vérifier l'existence d'une propriété, etc. elles ont en revanche deux avantages : la syntaxe plus simple (plus claire pour certains, plus obscure pour d'autres), et l'auto-génération du code par le compilateur.


    Mouais... Bien compris, mais pas convaincu ; je ne vois qu'une complication de la compréhension du code, parce qu'à  chaque fois qu'on verra "toto.machin = truc", on devra se poser la question de savoir si c'est une structure ou un objet qu'on modifie. Pour moi, le "." introduit donc carrément une incohérence de "type", à  mon avis c'est pire que cette histoire de "déférencement".

    Personnellement, les nouveautés d'Objective-C qui me bottent sont l'énumération rapide, les propriétés et les protocoles partiels, pour ce dernier, l'option qui me bottait le plus manque toujours à  l'appel, c'est-à -dire la possibilité d'écrire l'implémentation "par défaut" des méthodes optionnelles du protocole.
    Ce système aurait permis de définir une implémentation des méthodes déclarées comme @optional dans le protocole, cela aurait permis une plus grande flexibilité : la classe qui se conforme au protocole est assurée que toutes les méthodes optionnelles qu'elle n'implémente pas le sont et donc qu'on peut les utiliser, en revanche si l'utilisateur du protocole pense qu'il est possible de donner une meilleure implémentation, il lui suffit de réimplémenter la méthode. Pour l'instant je n'ai toujours pas vu ce système réintégré dans Objective-C 2.0, j'espère que je le retrouverai dans 7 jours. :(

    L'énumération rapide, ça peut être sympa, mais on va encore avoir droit à  de grandes interrogations : qu'est-ce qui est plus sûr, plus rapide etc. entre l'énumération rapide, le NSEnumerator et la boucle for  :) :p
    Les protocoles partiels reviennent à  implémenter l'héritage multiple et les classes / méthodes virtuelles ; pourquoi pas, j'attends encore d'y trouver une utilité  :P

    Sinon, je trouve le système des autorelease pools et de retain/release géniaux, mais je pense aussi que les deux systèmes sont à  utiliser avec attention. Je rejette l'affirmation qui consiste à  dire que les autorelease pools sont à  éviter au maximum, au contraire !


    Je maintiens qu'il vaut mieux utiliser :
    NSString *str = [[NSString alloc] initWithString:@&quot;test&quot;];<br />// ... Travail avec str<br />[str release];
    


    Que :
    NSString *str = [NSString stringWithString:@&quot;test&quot;];<br />// ... Travail avec str
    


    C'est plus lisible et plus efficace :P

    Dans tous les cas, il y a une chose qu'on ne doit pas oublier, et je pense que vous serez tous d'accord avec moi. Quelque soit le système qu'on souhaite employer, si on ne connaà®t les tenants et aboutissants, autant marcher à  l'aveuglette au bord d'un falaise...
    L'éducation des programmeurs y est pour beaucoup dans cette histoire, ce qui manque surtout à  mon avis, c'est le fait d'apprendre à  apprendre aux futurs programmeurs et aussi leur donner l'envie d'apprendre par eux-mêmes. Parce que bloquer des élèves dans une technologie parce que le marché l'exige n'est pas bon pour la suite surtout si le marché change radicalement (ce qui arrive souvent en informatique).


    ça c'est sûr...

    PS : Si vous avez survécu à  mon message jusqu'ici je vous félicite ! :D


    Toujours vivant  :kicking:
  • psychoh13psychoh13 Mothership Developer Membre
    03:53 modifié #7
    dans 1192876862:
    Mhhh, pas très sexy  :-\\ Ca veut dire que si on veut garder un objet, il ne suffit plus de faire un "retain" dessus, il faut en plus faire gaffe à  ce qu'il soit relié par un objet racine. Bof...


    En même temps, c'est un GC alors... Pas étonnant, c'est jamais très propre un GC. :D

    dans 1192876862:
    Mouais... Bien compris, mais pas convaincu ; je ne vois qu'une complication de la compréhension du code, parce qu'à  chaque fois qu'on verra "toto.machin = truc", on devra se poser la question de savoir si c'est une structure ou un objet qu'on modifie. Pour moi, le "." introduit donc carrément une incohérence de "type", à  mon avis c'est pire que cette histoire de "déférencement".


    Disons que le compilateur te fera la gueule si tu appelles une propriété sur un type qui ne la définit pas, ce qui oblige l'utilisation de typage statique.

    dans 1192876862:
    L'énumération rapide, ça peut être sympa, mais on va encore avoir droit à  de grandes interrogations : qu'est-ce qui est plus sûr, plus rapide etc. entre l'énumération rapide, le NSEnumerator et la boucle for  :) :p


    Bah ça c'est énumération rapide qui est plus rapide. Pour pouvoir rendre une classe énumérable rapidement il faut implémenter le protocole NSFastEnumeration qui ne contient qu'une seule méthode et qui sera donc utilisée pour énumérer. Elle est plus rapide à  mettre en place que NSEnumerator qui nécessite l'utilisation d'une deuxième classe (une sous-classe spécifique de NSEnumerator) et l'énumération est elle aussi plus rapide puisqu'on utilise qu'une seule méthode définie dans la classe elle-même et qui a donc un accès direct aux variables d'instance.
    Elle est aussi plus sécurisée car elle empêche la modification de la collection lors de l'énumération.

    Maintenant comparons for et for... in, je vais montrer 3 techniques d'utilisation des for et vous donner quelques avantages et inconvénients pour chaque :

    Considérons d'abord l'array suivant :

    NSMutableArray *mesChaines = [NSMutableArray arrayWithObjects: @&quot;chaine1&quot;, @&quot;test&quot;, @&quot;chaine&quot;, nil];
    


    Technique 1 - fast enumeration :
    for(NSString *string in mesChaines)<br />{ // traitement }
    


    Technique 2 - for (1) :
    int count = [mesChaine count];<br />for(int i = 0; i &lt; count; i++<br />{<br />&nbsp; &nbsp; [mesChaine objectAtIndex:i] // traitement<br />}
    


    Technique 3 - for (2) :
    for(int i = 0; i &lt; [mesChaine count]; i++<br />{<br />&nbsp; &nbsp; [mesChaine objectAtIndex:i] // traitement<br />}
    


    Technique 1 - fast enumeration :
    + syntaxe simple et claire
    + énumération sécurisé et complète de tous les éléments (une exception est levée si on tente de modifier la collection)
    + fonctionne avec n'importe quel type de collections
    - impossible de modifier la collection

    Technique 2 - for (1) :
    + aussi efficace voire plus que fast enumeration
    + possibilité de modifier les comportements selon la valeur de l'index
    - impossibilité de modifier la collection (supprimer/ajouter des éléments) sans risque, en effet la condition de sortie est toujours la même.
    - ne fonctionne qu'avec les NSArray car seul cette collection possède des index pour ses éléments

    Technique 3 - for (2) :
    + possibilité de modifier la collection en cours de route sans provoquer d'erreur (il suffit de décrémenté i en cas de suppression)
    + possibilité de modifier les comportements selon la valeur de l'index
    - ne fonctionne qu'avec les NSArray
    - un peu moins efficace que for (1) voire même de fast enumeration car il y a plus d'appels

    Au final donc, les for simples ne sont utilisables qu'avec les NSArray et permettent éventuellement de modifier la collection. Cependant, dans les cas normaux où la collection n'est pas modifiée ou bien que la collection n'est pas un NSArray, le fast enumeration reste la seule solution viable. Par contre la transformation d'un for...in par le compilateur est assez crade mais ça reste cacher. :D

    dans 1192876862:
    Les protocoles partiels reviennent à  implémenter l'héritage multiple et les classes / méthodes virtuelles ; pourquoi pas, j'attends encore d'y trouver une utilité  :P


    Les protocoles sont déjà  à  la base une alternative à  l'héritage multiple, la différence avec les méthodes optionnelles c'est qu'une pourra implémenter le protocole sans avoir l'obligation de fournir une implémentation spécifique pour chaque méthode.
  • schlumschlum Membre
    octobre 2007 modifié #8
    dans 1192880272:
    Disons que le compilateur te fera la gueule si tu appelles une propriété sur un type qui ne la définit pas, ce qui oblige l'utilisation de typage statique.


    Le compilateur oui, parce que lui il sait en une nanoseconde quoi est de quel type. Je parlais d'un (re)lecteur humain qui tombe sur ces "." ; l'effort intellectuel à  fournir pour savoir de quoi on parle sera plus important !

    Bah ça c'est énumération rapide qui est plus rapide. Pour pouvoir rendre une classe énumérable rapidement il faut implémenter le protocole NSFastEnumeration qui ne contient qu'une seule méthode et qui sera donc utilisée pour énumérer. Elle est plus rapide à  mettre en place que NSEnumerator qui nécessite l'utilisation d'une deuxième classe (une sous-classe spécifique de NSEnumerator) et l'énumération est elle aussi plus rapide puisqu'on utilise qu'une seule méthode définie dans la classe elle-même et qui a donc un accès direct aux variables d'instance.
    Elle est aussi plus sécurisée car elle empêche la modification de la collection lors de l'énumération.


    Je ne suis pas d'accord sur tout...
    1 - Quand on énumère un tableau, on évite de modifier en cours de route ce qu'on doit énumérer plus tard ; si on le fait c'est que le code est crade
    2 - Un autre thread peut éventuellement modifier le tableau, mais dans ce cas, on utilise les sections critiques, sinon le code est très mal foutu aussi
    Conclusion : quand le code est bien fait, l'argument de sécurité ne tient plus  :P

    Reste l'argument d'optimisation, et là  rien n'est sûr ; je pense que le moins optimisé c'est de passer par un NSEnumerator (cette lourdeur m'a toujours un peu gêné), mais c'est sûr qu'il y a un côté pratique (remplacé par le fast enumeration qui est encore plus pratique et n'a probablement pas le même problème d'optimisation).
    Donc tout se joue entre le "for" (en ne demandant le "count" qu'une fois), et le "fast enumeration". Il faudrait faire des tests poussés de performance ; à  mon avis ça se vaut.
    Et c'est sûr que la perspective de pouvoir énumérer des NSDictionary ou autre est alléchante.

    dans 1192876862:
    Les protocoles sont déjà  à  la base une alternative à  l'héritage multiple, la différence avec les méthodes optionnelles c'est qu'une pourra implémenter le protocole sans avoir l'obligation de fournir une implémentation spécifique pour chaque méthode.


    Les protocoles sont plutôt une alternative aux classes entièrement virtuelles, pas à  l'héritage multiple. Pourvoir fournir une implémentation par défaut est un pas supplémentaire vers l'héritage multiple. Par contre, je me demande bien comment ça serait possible vu qu'un protocole ne fournit en aucun cas des variables de classe. En gros pour faire une implémentation par défaut, on travaillerait sur quoi ?


    [Edit] Une question : est-ce qu'on peut programmer avec ces nouveautés et utiliser le SDK 10.3.9 ou 10.4u ?
  • Philippe49Philippe49 Membre
    03:53 modifié #9
    dans 1192875305:

    Ce n'est pas vraiment une officialisation des bindings. Car toute propriété ne représente pas forcément une variable d'instance, pas plus que toute variable d'instance n'est forcément représentée par une propriété. Il s'agit purement et simplement d'une simplification de la syntaxe des accesseurs/modificateurs.

    Le binding ne nécessite pas l'existence d'une variable d'instance, il suffit que les méthodes d'accès puisse être trouvées à  l'exécution. Voir ici.

    Pour les bindings, soit on a une variable d'instance, et alors l'implémentation des accessor méthods est optionnelle, soit il n'y a pas de variable d'instance et il faut implémenter des méthodes "setMyProperty" et "myProperty"

    et à  priori, c'est ce que tu fais avec la property NeedsDisplay ?





    dans 1192875305:

    Je suis tatillon oui, mais c'est juste pour préciser, la flèche "->" c'est uniquement dans le cas d'un pointeur sur une structure, pour une variable de type structure c'est bien sûr le point "." à  la base

    On peut donc penser que la flèche reste ce qu'elle a toujours été en C, c'est-à -dire le contenu d'une variable de la structure, atteinte par un pointeur sur la structure, contenu qui peut être-elle même une adresse, un objet, ou un
    type simple.


  • psychoh13psychoh13 Mothership Developer Membre
    octobre 2007 modifié #10
    dans 1192883164:
    Pour les bindings, soit on a une variable d'instance, et alors l'implémentation des accessor méthods est optionnelle, soit il n'y a pas de variable d'instance et il faut implémenter des méthodes "setMyProperty" et "myProperty"

    et à  priori, c'est ce que tu fais avec la property NeedsDisplay ?


    Certes, mais la différence à  priori c'est que les clés avec les bindings doivent correspondre avec les noms des méthodes ou des variables d'instance, alors que dans le cas des accesseurs, tu peux définir des noms totalement différents, de plus tu n'as pas la syntaxe rébarbative :

    [monObjet setValue: valeur forKey: maIVar];<br />valeur = [monObjet valueForKey: maIVar];
    


    Au lieu de tout ça on a ceci :
    monObjet.maIVar = valeur;<br />valeur = monObjet.maIVar;
    


    Ce qui est un peu plus simple à  écrire à  mon avis, et aussi plus protégé puisque tu pourras pas modifier la valeur d'une variable d'instance avec une propriété que t'invente toi-même. (sauf si tu utilises les catégories :D et encore).

    Donc les propriétés et les bindings se ressemblent étrangement mais je pense que question programmation objet les propriétés sont plus respectueuses.
  • octobre 2007 modifié #11
    Bon, chose promise, chose due: la censure sur cette discussion (berk, je déteste faire ça) est levée.

    Les propriétés
    avis mitigé, je ne trouve qu'en pratique que seule la partie "déclaration" est utile, tout ce qui se fait dans l'implémentation est très bof.

    La génération automatique d'accesseurs est certes sympatique (si on se contente de l'éditeur de texte de Xcode), mais comme souvent, un changement de variable induit d'autres changements (mise à  jour de l'interface par exemple), il faut quand même écrire son accesseur, donc bénéfice marginal.

    Pour ce qui est de la "dot notation", je n'aime pas. D'abord parce que je ne vois pas où est le problème d'écrire des crochets et surtout parce que j'aime bien qu'une notation corresponde à  un comportement. La dot notation est utilisée par les struct et les propriétés, mais le comportement dans les 2 cas est différent. Donc c'est mal.

    Autre truc que je leur reproche: typage statique obligé. Allez, encore une couche: seules les nouvelles classes utilisent les propriétés (il faut qu'une propriété soit explicitement déclarée comme telle dans le .h). Pas question de les utiliser avec les classes (et sous-classes) des "anciennes" classes.

    Bon il m'arrive quand même d'utiliser la dot notation, ceci dit. Uniquement dans ce genre de cas: myLayer.opacity /= 2.

    Les méthodes optionnelles des protocoles
    Là  que du bon. On définit par exemple quelques méthodes "primitives", qui sont elles requises, et plein d'autres méthodes optionnelles dont l'implémentation est faite dans une catégorie de NSObject sans interface mais qui se basent sur les méthodes requises. Pour prendre un exemple simpliste:

    [tt]@protocol GetItems
    @required
    -(NSUInteger)countOfItems;
    -(id)getObjectInItemsAtIndex:(NSUInteger)index;
    @optional
    -(NSArray*)items;
    -(id<NSFastEnumerator>)itemsEnumerator;
    @end

    @implementation NSObject (GetItemsProtocol)
    -(NSArray*)items { return [self valueForKey:@items]; }
    -(id<NSFastEnumerator>)itemsEnumerator { return [(id<GetItems>self items]; }
    // le array renvoyé se conforme à  NSFastEnumeration
    @end[/tt]

    Si on a maintenant un classe qui peut fournir des élements et qui se base sur un array, on réimplémente les 4 méthodes pour se baser sur l'array:
    [tt]-(NSUInteger)countOfItems { return [_theItems count]; }
    -(id)objectInItemsAtIndex:(NSUInteger)index { return [_theItems objectAtIndex:index]; }
    -(NSArray*)items { return _theItems; }[/tt]

    Si on a maintenant un objet qui se fournit à  partir d'un CFArray, là  il suffit de réimplémenter les 2 méthodes requises, mais on pourra utiliser en toute transparence les autres méthodes optionnelles vu qu'elles ont été implémentées dans une catégorie de NSObject. Pour des questions de performances, on pourra aussi implémenter le protocole NSFastEnumeration pour la classe en question et alors renvoyer [tt]self[/tt] pour la méthode [tt]itemsEnumerator[/tt] (là  on peut voir l'avantage de définir "l'énumeration" sur un protocole plutôt qu'une classe).

    L'implémentation du protocole "regrettée" par psycho n'est donc pas un manque en pratique, autant utiliser ce qui existe.

    Ah oui, point intéressant: NSEnumerator se conforme à  NSFastEnumeration (ce n'est pas encore écrit dans la doc, mais bien dans NSEnumerator.h). Assez pratique si on veut énumérer un tableau à  l'envers par exemple (ou les clés un NSDictionary), on peut donc parfaitement écrire:
    [tt]for ( id obj in [array reverseObjectEnumerator] )[/tt]

    Le garbage collector: un a priori assez négatif à  la base, mais quand on voit Xcode 3 et Safari 3 (pas la beta Tiger, le vrai), cette question mérite d'être approfondie.

    One more thing:
    Si vous ne comptez pas diffuser vos applis pour d'autres systèmes que Leopard (surtout dans le cas où on utilise CoreAnimation), je vous conseille d'ajouter le "C Flag" NS_BUILD_32_LIKE_64 dans vos paramètres de compil. Tout gros avantage: les CGRect (utilisés dans CoreAnimation) et les NSRect (utilisés dans l'AppKit) seront alors parfaitement interchangeables.
  • psychoh13psychoh13 Mothership Developer Membre
    03:53 modifié #12
    Euh... Pour les CGRect et NSRect, j'ai ru voir dans les .h que NSRect était maintenant définit comme un CGRect...
  • 03:53 modifié #13
    Regarde un peu plus haut, cette ligne a du t'échapper...
    [tt]#if __LP64__ || NS_BUILD_32_LIKE_64[/tt]


  • psychoh13psychoh13 Mothership Developer Membre
    novembre 2007 modifié #14
    Arf ça m'apprendra à  lire jusqu'au bout... Toujours est-il que je ne comprends pas pourquoi ils n'ont pas fait ce genre de truc plus tôt...
  • psychoh13psychoh13 Mothership Developer Membre
    03:53 modifié #15
    Bon, je relance le sujet pour faire une remarque à  propos de cette réponse  ;D :

    dans 1193413303:
    Allez, encore une couche: seules les nouvelles classes utilisent les propriétés (il faut qu'une propriété soit explicitement déclarée comme telle dans le .h). Pas question de les utiliser avec les classes (et sous-classes) des "anciennes" classes.


    Bah non justement ! Tu peux utiliser des propriétés sur les classes qui ne les définit pas explicitement !

    Essaye ça :

    NSString *string = @&quot;string&quot;;<br /><br />NSLog(@&quot;Taille : %d, ref count = %d, objet = %@, classe = %@&quot;, string.length, string.retainCount, string.self, string.class);
    


    ;)
  • 03:53 modifié #16
    dans 1195771872:

    Bah non justement ! Tu peux utiliser des propriétés sur les classes qui ne les définit pas explicitement !
    J'ai remarqué entretemps. ça n'a pas toujours été le cas en fait.
  • Philippe49Philippe49 Membre
    03:53 modifié #17
    dans 1195771872:

    Bah non justement ! Tu peux utiliser des propriétés sur les classes qui ne les définit pas explicitement !


    Où peut-on trouver dans la doc les propriétés implicites des classes standards ?
    On s'attend à  trouver celles qui sont compatibles avec KVC, mais length pour une NSString ?
  • psychoh13psychoh13 Mothership Developer Membre
    03:53 modifié #18
    dans 1195911743:
    Où peut-on trouver dans la doc les propriétés implicites des classes standards ?


    À priori nul part

    dans 1195911743:
    On s'attend à  trouver celles qui sont compatibles avec KVC, mais length pour une NSString ?


    Après quelques tests, il est possible d'appeler n'importe quelle méthode ne prenant aucun argument en utilisant la notation avec le point, il suffit simplement de donner le nom.
    Sinon pour les méthodes avec un seul argument, à  priori seules les méthodes ayant le mot "set" au début fonctionnent comme une propriété, il suffit d'enlever le "set" pour que ça marche.

    En tout cas cela marche avec toutes les instances, donc pas avec les classes, mais ça marche même sur self et super, voici le code que j'ai pu pondre, il s'agit d'une simple sous-classe de NSView :

    @implementation MyView<br /><br />- (id)initWithFrame:(NSRect)frame<br />{<br />&nbsp; &nbsp; self = [super initWithFrame:frame];<br />&nbsp; &nbsp; if (self)<br />&nbsp; &nbsp; {<br />&nbsp; &nbsp; }<br />&nbsp; &nbsp; return self;<br />}<br /><br />- (void)awakeFromNib<br />{<br />&nbsp; &nbsp; self.window.backgroundColor = [NSColor greenColor];<br />}<br /><br />- (void)drawRect:(NSRect)rect<br />{<br />&nbsp; &nbsp; NSColor *color = [NSColor blueColor];<br />&nbsp; &nbsp; color.set;<br />&nbsp; &nbsp; [NSBezierPath bezierPathWithRect:super.bounds].fill;<br />&nbsp; &nbsp; <br />&nbsp; &nbsp; color = [NSColor redColor];<br />&nbsp; &nbsp; color.set;<br />&nbsp; &nbsp; [NSBezierPath bezierPathWithRect:NSMakeRect(10, 10, 10, 10)].fill;<br />}<br /><br />@end
    
  • muqaddarmuqaddar Administrateur
    03:53 modifié #19
    Un peu de déterrage ne fait pas de mal...

    Ayant quitté objective-c 1.x il y a 3 ans, qu'elle n'est pas ma surprise de voir ça dans les classes SDK iPhone :

    [window addSubview:tabBarController.view];
    


    alors que je peux écrire ça qui m'est beaucoup plus familier :

    [window addSubview:[tabBarController view]];
    


    Je pense que c'est arrivé avec l'objective-C 2... que j'ai loupé...
    Bref, est-ce une hérésie de coder "comme avant" ? :o
  • Philippe49Philippe49 Membre
    03:53 modifié #20
    Ce n'est pas une hérésie , mais c'est vrai que quand on parcourt les exemples iPhone, on voit très souvent le modèle tabBarController.view.

    Avec des petis écueils quand même
  • psychoh13psychoh13 Mothership Developer Membre
    03:53 modifié #21
    Bah non pas du tout...

    Moi personnellement, j'utilise de préférence l'ancienne syntax, cependant...

    Mon message en général, pour garder le code consistant, c'est de dire aux gens d'utiliser la syntax [] avec toutes les méthodes qui ne représentent pas une propriété ou une ivar de la classe, et d'utiliser la syntaxe avec point comme accesseurs...

    Donc moi je suggère :
    view.bounds;<br />[super init];
    


    et pas :
    [view bounds];<br />super.init;
    
  • Philippe49Philippe49 Membre
    03:53 modifié #22
    Tiens , bonjour Psycho ! 
  • psychoh13psychoh13 Mothership Developer Membre
    03:53 modifié #23
    Tiens, bonjour Philippe !
  • Philippe49Philippe49 Membre
    03:53 modifié #24
    :p
    Cela fait quelques temps maintenant ... occupé ?
    Il y a quelques posts qui se sont déroulés depuis pour lesquelles tes réponses auraient été profitables !

  • psychoh13psychoh13 Mothership Developer Membre
    03:53 modifié #25
    O RLY ?

    Le seraient-ils toujours ? Il n'est jamais trop tard pour bien faire. :D
  • Philippe49Philippe49 Membre
    03:53 modifié #26
    RLY
  • psychoh13psychoh13 Mothership Developer Membre
    03:53 modifié #27
    Sur quel(s) sujet(s) par exemple ? :D
  • schlumschlum Membre
    03:53 modifié #28
    Même pour les accesseurs, c'est moche quand même  :P
    ça serait "->" encore...
  • schlumschlum Membre
    03:53 modifié #29
    dans 1239491055:

    Sur quel(s) sujet(s) par exemple ? :D


    Ceux là  ?  :)
    http://www.osx-dev.com/index.php?action=unread
  • Philippe49Philippe49 Membre
    03:53 modifié #30
    dans 1239491055:

    Sur quel(s) sujet(s) par exemple ? :D

    Là  tu m'en demande trop à  1 heure du matin !
    Plus généralement, depuis quelques temps on a beaucoup de "nouveaux" qui arrivent sur le site et c'est très bien, mais les discussions restent assez techniques. C'est dommage.
  • AliGatorAliGator Membre, Modérateur
    03:53 modifié #31
    Pareil moi j'utilise la syntaxe pointée pour les propriétés ou ivars... il ne me serait même pas venu à  l'idée de coder une méthode genre "init" sous forme de propriété pour y accéder par "self.init"... ça me parait même une hérésie :P

    Donc la syntaxe pointée moi je l'aime bien pour accéder à  des variables d'instances qui sont vraiment des "propriétés" de l'objet au sens courant du terme (la backgroundColor d'une UIView sa frame, ses bounds...), dans ce cas ça a un sens de les coder sous forme de @property dans le .h ce qui permet donc ensuite d'utiliser la "syntaxe pointée"... si on le veut ;)

    Mais rien ne nous y oblige. En fait déclarer "@property Type var;" dans le .h revient à  2 choses :
    1) faire comme si on avait déclaré -(Type)var; et -(void)setVar:(Type)v; (et si on met @synthesize dans le .m ça va coder tout seul ces 2 méthodes, getter et setter), et après tu peux très bien les utiliser "comme avant", avec la syntaxe crochetée v=[self var]; et [self setVar:v];...
    2) rajouter la possibilité d'utiliser la syntaxe pointée sur cette variable de ton objet, donc "v=self.var;" étant équivalent à  appeler le getter v=[self var] et "self.var=v;" étant équivalent à  appeler le setter [self setVar:v];

    Mais si tu veux te cantonner au (1) et donc les utiliser comme des méthodes "normales", comme au bon vieux temps d'avant Objective-C 2.0 y'a aucun souci t'as tout à  fait le droit !
Connectez-vous ou Inscrivez-vous pour répondre.