Comment hérite-t-on d'une méthode de classe ?

colas_colas_ Membre
avril 2013 modifié dans Objective-C, Swift, C, C++ #1

J'ai une classe B qui hérite d'une sous-classe de A.


J'ai une méthode de A : 


 



+ (id) insertInManagedObjectContext

 


Je souhaite la réécrire dans B.

Est-ce que je peux écrire 



(1)

+ (id)insertInManagedObjectContext
{
self = [super insertInManagedObjectContext] ;
// ...
return self ;
}

? Et, si non, pourquoi ?


 


Ou dois-je plutôt écrire :


 



(2)

+ (id)insertInManagedObjectContext
{
id myNewInstance = [super insertInManagedObjectContext] ;
// ...
return myNewInstance ;
}

 


? Le problème de cette méthode est que myNewInstance étant un id, le compilteur ne validera si j'applique à  myNewInstance des méthodes spécifiques à  classeA ou classeB.


 


J'imagine que



(3)

+ (id)insertInManagedObjectContext
{
classeA *myNewInstance = [super insertInManagedObjectContext] ;
// ...
return myNewInstance ;
}


 


est faux, par ailleurs ; car le type attendu par la méthode est (id) et non (classeA).


 


Donc, la bonne solution est-elle



(4)

+ (id)insertInManagedObjectContext
{
classeB *myNewInstance = [super insertInManagedObjectContext] ;
// ...
return myNewInstance ;
}


 


?


Merci !


 


 


Réponses

  • Dans classe B:



    + (id)insertInManagedObjectContext
    {
    [ClasseA insertInManagedObjectContext] ;
    // ...
    return self ; // ici self est ClasseB quelque soit la méthode appelée (un peu logique non ?)
    }
  • LexxisLexxis Membre
    avril 2013 modifié #3

    insertInManagedObjectContext te renverra un objet de type A. Tu dois pouvoir caster le retour en type B (ou id) à  condition qu'il n'y ai pas de variables d'instance dans B (étant donné qu'il n'y aura pas eu d'allocation d'instance de B .


    (4) me paraà®t donc valide sous certaine condition... 


  • Alors déjà , sache que les 4 propositions fonctionnent et fourniront le résultat que tu attends. Un petit Gist pour le prouver : https://gist.github.com/JegnuX/01cbca65d3deca1b0808

    Cependant il y a bien certaine proposition qui sont vraiment à  proscrire :
    (1)

    + (id)insertInManagedObjectContext
    {
    self = [super insertInManagedObjectContext] ;
    // ...
    return self ;
    }
    Ici "self" fait référence à  ta ClasseB. En objective-C, les classes sont aussi des objets de type "Class".
    Donc ça fonctionne car grâce au typage dynamique tu peux assigner n'importe quel objet à  ta variable, mais changer la valeur de self est vraiment une très mauvaise idée ici. Je sais pas si ça peut planter ou provoquer des comportements inattendu, mais c'est vraiment à  ne pas faire.
    (2)

    + (id)insertInManagedObjectContext
    {
    id myNewInstance = [super insertInManagedObjectContext] ;
    // ...
    return myNewInstance ;
    }
    Ici c'est techniquement le code le plus correct. Mais comme tu le souligne, ton objet n'étant pas typé, quand tu appelleras des méthodes de ta ClasseB sur ton objet, tu auras potentiellement des warnings, voire des erreurs de compilation si tu utilises ARC.
    (3)

    + (id)insertInManagedObjectContext
    {
    classeA myNewInstance = [super insertInManagedObjectContext] ;
    // ...
    return myNewInstance ;
    }
    Ici c'est juste un problème de sémantique. C'est correct car l'objet retourné est une sous-classe de ta ClasseA, donc tu n'auras aucune soucis. C'est juste pas vraiment logique d'utiliser ce code dans ta ClasseB. De plus, comme avec "id" tu ne pourras pas appeler des méthodes de ta ClasseB.
    (4)

    + (id)insertInManagedObjectContext
    {
    classeB myNewInstance = [super insertInManagedObjectContext] ;
    // ...
    return myNewInstance ;
    }
    Compte tenu de tout ce qui a été dit, c'est effectivement la meilleure solution. Tu pourras appeler sur ton objet n'importe quelle méthode de ta classeB.

  • (4)

    + (id)insertInManagedObjectContext
    {
    classeB myNewInstance = [super insertInManagedObjectContext] ;
    // ...
    return myNewInstance ;
    }

    Compte tenu de tout ce qui a été dit, c'est effectivement la meilleure solution. Tu pourras appeler sur ton objet n'importe quelle méthode de ta classeB.

     


     


    Merci !

  • Ca ne ressemble pas en grand chose cette méthode.


    Ca pourrait ressembler à  une méthode de commodité, mais ça ne respecte pas les règles de nommage.


     


    Une méthode de commodité porte le nom de la classe à  instancier.


     



    @implementation ClassA

    -(id) init
    {
    if([self=[super init])
    {

    }

    return self;
    }

    +(id) classA
    {
    return [[[ClassA alloc] init] autorelease]; // retourne un objet de classe A
    }

    @end

    @implementation ClassB : ClassA

    -(id) init
    {
    if([self=[super init]) // appel init de ClassA
    {

    }

    return self;
    }

    +(id) classB
    {
    return [[[ClassB alloc] init] autorelease]; // retourne un objet de classe B
    }

    @end
  • AliGatorAliGator Membre, Modérateur

    Je ne suis pas d'accord avec ton implémentation de ta méthode de commodité mpergrand


    Justement je mettrais pas [ClassA alloc] mais [self alloc]


    Comme ça si tu fais une sous-classe SousClasseA qui hérite de ClassA, quand tu vas appeler la méthode de commodité sur SousClasseA ça va bien créer une instance de SousClasseA et pas de ClassA


     


    Par exemple si tu appelles [NSMutableString string] ça te retourne bien une instance de NSMutableString, et pas de NSString,pourtant la méthode +string n'est sans doute implémentée que dans NSString (avec un "return [[[self alloc] init] autorelease];") et pas surchargée dans NSMutableString.


    Elle serait implémentée avec un "return [[[NSString alloc] init] autorelease];" alors un [NSMutableString string] retournerait une instance de NSString et pas de NSMutableString.


  • mpergandmpergand Membre
    avril 2013 modifié #8

    Salut Aligator,


    je mettrais pas [ClassA alloc] mais [self alloc]


     


    Je suis d'accord avec toi et c'est ce que j'avais fait initialement, mais ici on a une méthode qui désigne un type de classe précis( classA et classB) et une méthode classA qui retourne un objet de classB c'est assez bizarre ...

    Le but était aussi de montrer l'aspect statique des méthode de classes.

    Une autre solution, bien meilleur, serait d'implémenter par exemple une méthode new:




    @implementation ClassA

    -(id) init
    {
    if([self=[super init])
    {

    }

    return self;
    }

    +(id) new
    {
    return [[self alloc] init];
    }

    @end

    @implementation ClassB : ClassA

    -(id) init
    {
    if([self=[super init]) // appel init de ClassA
    {

    }

    return self;
    }


    et voila, mais c'est un peu ballot car new existe déjà  pour NSObject :)

    et en regardant la doc, il y a un exemple intéressant d'utilisation de new:


    However, there's little point in implementing a new... method if it's simply a shorthand for alloc and init..., as shown above. Often new... methods will do more than just allocation and initialization. In some classes, they manage a set of instances, returning the one with the requested properties if it already exists, allocating and initializing a new instance only if necessary. For example:

     



    + newMyClassWithTag:(int)tag data:(struct info *)data

    {

    MyClass *theInstance;



    if ( theInstance = findTheObjectWithTheTag(tag) )

    return [theInstance retain];

    return [[self alloc] initWithTag:tag data:data];

    }


     


    dont colas2 pourrait peut-être s'inspirer si tant est que je puisse comprendre ce que colas2 veut faire vraiment ...


  • AliGatorAliGator Membre, Modérateur

    Oui en fait en rédigeant ma réponse, j'étais sûr que tu allais me dire ça, que puisque la méthode s'appelle classA ça ferait bizarre de retourner une instance d'autre chose que ClassA ;)


     


    Mais en fait ça dépend des vrais noms de classA et classB.


    C'est d'ailleurs pour ça que j'ai pris l'exemple ensuite de NSMutableString vs. NSString. Si le vrai terme à  la place de "classB" c'est un terme qui est bien un sous-ensemble de "classA" par exemple si tu remplaces "classA" par "Parser" et ses sous-classes classB et classC par "XMLParser" et "JSONParser", alors c'est logique que la méthode "+[Parser parser]" retourne un XMLParser ou un JSONParser selon si elle est appelée sur la sous-classe correspondante, plutôt que de retourner une instance de la classe mère Parser dans laquelle est implémentée la méthode "parser".


     


    Et en pratique j'ai rarement voire jamais vu de cas où il était utile de créer une méthode de classe qui retourne une classe spécifique (une des classes parentes explicitement) plutôt que la classe sur laquelle on l'appelle.


    Quel intérêt si tu as ClassB qui hérite de ClassA d'avoir une méthode "classA" définie dans ClassA... qui retourne forcément un ClassA dans tous les cas ? Je veux dire si tu veux une instance de ClassA, tu vas pas faire [ClassB classA] en te disant que même appelé sur la sous-classe ClassB ça retournera un ClassA... quitte à  vouloir une instance de ClassA, tu vas appeler [ClassA classA], pas [ClassB classA], non ? Donc j'ai du mal à  voir un cas d'usage où ça serait utile.


  • mpergandmpergand Membre
    avril 2013 modifié #10

    Donc j'ai du mal à  voir un cas d'usage où ça serait utile.


     



    Oui, moi aussi, c'était un exemple très théorique ...


     


    Et d'ailleurs, je me suis dit que c'est ce que l'on ferait en C++ ou en Java ou le mot clef this ne peut s'utiliser que sur une instance (si je ne me trompe pas)


     


    En c++ on utilise l'opérateur ::


    ce qui en ObjC s'écrit:  [NomClasse nomMéthode]


    s'écrirait en C++:  NomClasse::nomMéthode()


     


    Ca n'a pas la souplesse du self en ObjC, c'est vraiment du statique !


  • colas_colas_ Membre
    avril 2013 modifié #11

    dont colas2 pourrait peut-être s'inspirer si tant est que je puisse comprendre ce que colas2 veut faire vraiment ...




    Overrider dans une sous-classe la méthode + (id)insertInManagedObjectContext:(NSManagedObjectContext*)moc_;

    écrite par moGenerator.
Connectez-vous ou Inscrivez-vous pour répondre.