Comment écrit-on une méthode de classe ?

arnolix

Membre confirmé
15 Juillet 2003
80
3
Voilà : j'ai un objet qui a pour classe une sous classe de NSObject que j'intitule ici myClass.
Cette classe comporte comme champ un NSMutableArray. J'ai donc :

Bloc de code:
@interface myClass : NSObject  {
	NSMutableArray *tab;
}

Je voudrais faire la chose suivante, très classique certainement, mais que néanmoins je ne sais pas faire et je ne trouve rien à ce sujet.

lorsque je fais :

Bloc de code:
newObject = [myClass alloc]
,

je crée une instance de myClass. Mais j'aimerais que newObject ait déja son champ tab alloué, de sorte que lors de l'initialisation de newObject je n'ai qu'à remplir le tableau tab. Autrement dit dans l'implémentation de myClass je veux une méthode init de la forme :

Bloc de code:
- (id)init
{
	self = [super init];
	if(self) [tab addObject:......]; //tab est déjà alloué
	return self;
}
Donc, si j'ai bien compris, il me faut sous-classer la méthode de classe + (id)alloc dans l'implémentation de myClass :
Bloc de code:
+ (id)alloc
{
	[[self class] alloc];
	tab = [NSMutableArray alloc];
	if(self & tab) return self else return nil;
}
Ce code est-il correct ? Je me dis que [self class] ne doit pas fonctionner puisque self n'existe pas encore !
 
Bonjour,

je ne vois pas pourquoi tu veux surcharger alloc. La fonction init suffit :

myClass *newObject;

newObject = [[myClass alloc] init];

et dans init tu mets l'allocation et l'initialisation de tes membres :

- (id)init
{
self = [super init];
if(self)
{
tab = [[NSMutableArray alloc] initWithCapacity: 100];

[tab addObject:......];
}

return self;
}

Je n'ai jamais eu besoin de réécrire la fonction alloc et je ne suis pas pressé de me lancer dans cette aventure :)
 
Bien sur que on peut le faire dans init, et je ne m'en prive pas pour autant. Cependant il me paraît logique que la mise en place de tab relève plutôt d'une allocation que d'une initialisation. Sinon pourquoi fait-on la distinction entre +alloc et -init ?

Mon but c'est bien d'essayer de sous-classer une méthode de classe. Pas de l'éviter :)

Est-ce à ce point ésotérique comme question :confused:?

Le sample code, fournit avec le bundle Xcode, qui s'appelle Sketch, prétend être un bon exemple de prog en obj-C . On y trouve des exemples de sous-classement de méthode de classe. Mais je les comprend pas bien

Bloc de code:
+ (NSCursor *)creationCursor {
    // By default we use the crosshair cursor
    static NSCursor *crosshairCursor = nil;
    if (!crosshairCursor) {
        NSImage *crosshairImage = [NSImage imageNamed:@"Cross"];
        NSSize imageSize = [crosshairImage size];
        crosshairCursor = [[NSCursor allocWithZone:[self zone]] initWithImage:crosshairImage hotSpot:NSMakePoint((imageSize.width / 2.0), (imageSize.height / 2.0))];
    }
    return crosshairCursor;
}

C'est quoi ce static NSCursor ? Et pourquoi écrire if (!crosshairCursor) juste après alors qu'on vient de lui affecter la valeur nil ?
 
La fonction creationCursor alloue et initialise un objet statique lors du premier appel quand la variable statique est encore nulle et la renvoie. Aux appels suivants, si la variable n'a pas été désallouée, elle renverra toujours la même.

Tu peux noter que pour créer l'objet NSImage, on utilise imageNamed qui combine alloc, init et autorelease (voir la doc d'Apple).

Tu peux créer facilement des fonctions du genre creationCursor ou imageName. Mais elles ne sont pas équivalentes à un alloc. Celui-ci doit appeler les fonctions d'allocation de pointeur C (malloc ou autres) et gérer le compteur d'instance (voir la gestion de mémoire en Cocoa Obj-C). Et cela ne doit donc avoir rien d'évident ;)
 
ntx a dit:
La fonction creationCursor alloue et initialise un objet statique lors du premier appel quand la variable statique est encore nulle et la renvoie. Aux appels suivants, si la variable n'a pas été désallouée, elle renverra toujours la même.

Tu peux noter que pour créer l'objet NSImage, on utilise imageNamed qui combine alloc, init et autorelease (voir la doc d'Apple).

Tu peux créer facilement des fonctions du genre creationCursor ou imageName. Mais elles ne sont pas équivalentes à un alloc. Celui-ci doit appeler les fonctions d'allocation de pointeur C (malloc ou autres) et gérer le compteur d'instance (voir la gestion de mémoire en Cocoa Obj-C). Et cela ne doit donc avoir rien d'évident ;)

Merci . Alors si j'ai bien compris, lorsqu'on appelle ne deuxième fois cette méthode, la variable *crosshairCursor n'est pas remise à nil ?

Quand tu dis que cette méthode n'est pas du type +alloc car elle n'use pas d'allocation de pointeur, je ne suis pas vraiment convaincu : que fait alors [NSCursor allocWithZone:[self zone]] si ce n'est gérer de l'espace mémoire ?

Ou alors peux-tu m'éclairer un peu plus sur ce point ?

Quand à gérer le compteur d'instance, ne suffit-il pas de l'incrémenter tout simplement ? Ou encore reléguer cette tache à la classe parente en utilisant un truc du genre [super alloc] ?
 
arnolix a dit:
Quand tu dis que cette méthode n'est pas du type +alloc car elle n'use pas d'allocation de pointeur, je ne suis pas vraiment convaincu : que fait alors [NSCursor allocWithZone:[self zone]] si ce n'est gérer de l'espace mémoire ?
Certe mais il y a un init derrière, non ? Il faut voir tout le bloc d'instruction.
En fait si tu compares au C++, je dirais que alloc est équivalent à l'opérateur new qui se charge de reserver l'espace en mémoire (en appelent des malloc C; il n'y pas de compteur de référence en C++), et init au constructeur de la classe qui se charge d'initialiser les membres de la classe (entre autre).
Avoues tout de même qu'on surcharge rarement un opérateur new (en tout cas moi je ne l'ai jamais fait :)) alors que la grande majorité des classes ont un constructeur.

arnolix a dit:
Quand à gérer le compteur d'instance, ne suffit-il pas de l'incrémenter tout simplement ? Ou encore reléguer cette tache à la classe parente en utilisant un truc du genre [super alloc] ?
Tout a fait tu peux réécrire ta fonction alloc en commençant par super alloc et compléter avec ce que tu veux derrière. Mais je pense que ce code serait tout de même mieux dans la fonction init ;) Jettes un coup d'oeil sur des exemples de code Obj-C et tu verra qu'il n'y a que des fonctions init, jamais d'alloc ... ou vraiment très rarement.
 
arnolix a dit:
Quand à gérer le compteur d'instance, ne suffit-il pas de l'incrémenter tout simplement ? Ou encore reléguer cette tache à la classe parente en utilisant un truc du genre [super alloc] ?

Il semble que ce que tu vroudrais, c'est avoir une variable de classe permettant de stocker toutes les instances de cette classe ?

Les variables et les méthodes de classes sont souvent appelés static dans d'autres languages (ex Java) mais attention en objc static n'existe pas c'est du C et donc n'a rien à voir avec les classes.

En C il existe deux types de variables static, les variables static globales à un fichier, accessiblent uniquement par les fonctions définies dans ce fichier et les variables static locales à une fonction (cas de static NSCursor)

Pour avoir une variable de classe stockant toutes les instances d'une classe, en Java on pourrait faire comme ceci:

Bloc de code:
class MaClasse {

  static NSMutableArray instances;  // variable de classe unique à toutes les instances

  static {   // exécuté une seule fois lors de la 1ere utilisation de cette classe

    instance=new NSMutableArray();  
  }

  public Maclasse() {  // constructeur
    
    super()
    instances.addObject(this);  // stocke l'instance créée
  }

le bloc static est inutile on aurait pu écrire directement: static NSMutableArray instances=new NSMutableArray(); mais c'est pour mieux coller à l'équivalent en objc qui va suivre.

En objc, on ne peut pas créer de variables de classe (sauf erreur de ma part) on a donc recours à une variable static en C, donc en objc :
Bloc de code:
static NSMutableArray* instances;

@implementation MaClasse

// exécuté une seule fois lors de la 1ere utilisation de cette classe
// équivalent du bloc static Java

+(void) initialize 	
{

	instances=[NSMutableArray alloc]init];
	
}


 - (id)init
 {
         self = [super init];
         if(self) 
         {
 
            [instances addObject:self];
         }
 
         return self;
 }

Ne pas oublier que objc est une syntaxe rajoutée par dessus C, comme le montre l'utilisation de static ici.
 
Autre solution :

1) écrire une classe définissant les objets myClass

2) écrire une class "manager": myClassManager qui gère le tableau des instances. Pour assurer l'unicité de l'instance de cette classe, il suffit de l'implémenter en utilisant le design pattern singleton. Cette classe fournit une fonction init, addInstance et getInstances, et le tour est joué.

La fonction init de la classe myClass n'a plus qu'à appeler addInstance.
 
La j'ai un peu de mal à suivre. Pensais en effet que c'était plus simple que cela. D'autre part étant autodidacte je ne connais pas le java ni le c++ :(. La prog objet m'interesse bcp mais je la découvre avec objC. Ma question etait donc plus portée sur un pb de progrmmation objet.

Bon cependant je crois comprendre qu'en fait que ce n'est pas d'une variabe de classe que je voulais. Bien que ton code mpergrand sois très instructif. l'usage de static est encore un peu nébuleux pour moi.

Tout compte fait ntx t'a raison. construire mon tab dans init ca suffit amplement.

Merci pour vos réponses.
 
J'oubliais : ou peut-on trouver des infos sur les design-patterns (autres que MVC bien entendu) ? Sur singleton par exemple ? Il me semple avoir compris qu'en prog objet il faut suivre des design patterns pour éviter d'avooir du code qui devienne un foutoir complet.
 
Pour avoir des infos en programmation en général : Goggle tout simplement. En demandant "singleton objective c" tu devrais trouver ton bonheur. Sinon un site dédié : Cetus Link ou encore, mais plus hardu : Design Patterns, Pattern Languages, and Frameworks

L'usage des design patterns n'est pas obligatoire, mais cela resoud avec élégance des problèmes auxquels on peut être rapidement confrontés. Dans le cas du singleton, il donne une classe avec un instance unique. Tu as des exemples dans Cocoa : NSNotificationCenter et sa méthode defaultCenter qui permet d'accéder à son instance unique. Cela remplace les variables globales utilisées couramment en C.