Objectif c - Objective-C

Objectif c
Famille C
Conçu par Tom Love et Brad Cox
Première apparition 1984 ; il y a 37 ans ( 1984 )
Version stable
2.0
Discipline de frappe statique , dynamique , faible
Système d'exploitation Multiplateforme
Extensions de nom de fichier .h, .m, .mm, .M
Site Internet développeur.apple.com
Les principales mises en œuvre
Clang , CCG
Influencé par
C , Petite conversation
Influencé
Groovy , Java , Nu , Objective-J , TOM , Swift

Objective-C est un usage général , orienté objet langage de programmation qui ajoute Smalltalk de style messagerie à la C langage de programmation. Développé à l'origine par Brad Cox et Tom Love au début des années 1980, il a été sélectionné par NeXT pour son système d'exploitation NeXTSTEP . Objective-C était le langage de programmation standard pris en charge par Apple pour développer des applications macOS (qui descend de NeXTSTEP) et iOS à l' aide de leurs interfaces de programmation d'applications (API) respectives, Cocoa et Cocoa Touch , jusqu'à l'introduction de Swift en 2014.

Les programmes Objective-C développés pour des systèmes d'exploitation non Apple ou qui ne dépendent pas des API d'Apple peuvent également être compilés pour toute plate-forme prise en charge par GNU GCC ou LLVM / Clang .

Les fichiers de programme de « messagerie/mise en œuvre » du code source Objective-C ont généralement des extensions de nom de fichier .m , tandis que les fichiers « en-tête/interface » Objective-C ont des extensions .h , les mêmes que les fichiers d'en-tête C. Les fichiers Objective-C++ sont désignés par une extension de fichier .mm .

Histoire

Objective-C a été créé principalement par Brad Cox et Tom Love au début des années 1980 dans leur entreprise Productivity Products International (PPI) .

Avant la création de leur entreprise, tous deux avaient été initiés à Smalltalk alors qu'ils travaillaient au Centre de technologie de programmation d' ITT Corporation en 1981. Les premiers travaux sur Objective-C remontent à cette époque. Cox était intrigué par les problèmes de véritable réutilisation dans la conception et la programmation de logiciels . Il s'est rendu compte qu'un langage comme Smalltalk serait inestimable dans la création d'environnements de développement pour les développeurs de systèmes chez ITT. Cependant, lui et Tom Love ont également reconnu que la rétrocompatibilité avec C était d'une importance cruciale dans le milieu de l'ingénierie des télécommunications d'ITT.

Cox a commencé à écrire un préprocesseur pour C pour ajouter certaines des capacités de Smalltalk. Il eut bientôt une implémentation fonctionnelle d'une extension orientée objet du langage C , qu'il appela « OOPC » pour Object-Oriented Pre-Compiler. Love a été embauché par Schlumberger Research en 1982 et a eu l'opportunité d'acquérir la première copie commerciale de Smalltalk-80, ce qui a encore influencé le développement de leur idée originale. Afin de démontrer que de réels progrès pouvaient être réalisés, Cox a montré que la réalisation de composants logiciels interchangeables ne nécessitait en réalité que quelques modifications pratiques des outils existants. Plus précisément, ils devaient prendre en charge les objets de manière flexible, être fournis avec un ensemble de bibliothèques utilisables et permettre au code (et à toutes les ressources nécessaires au code) d'être regroupés dans un format multiplateforme.

Love et Cox ont finalement formé PPI pour commercialiser leur produit, qui associait un compilateur Objective-C à des bibliothèques de classes. En 1986, Cox a publié la description principale d'Objective-C sous sa forme originale dans le livre Object-Oriented Programming, An Evolutionary Approach . Bien qu'il ait pris soin de souligner que le problème de la réutilisabilité ne se limite pas à ce que fournit Objective-C, le langage s'est souvent trouvé comparé fonctionnalité pour fonctionnalité avec d'autres langages.

Vulgarisation via NeXT

En 1988, NeXT a licencié Objective-C de StepStone (le nouveau nom de PPI, le propriétaire de la marque Objective-C) et a étendu le compilateur GCC pour prendre en charge Objective-C. NeXT a développé les bibliothèques AppKit et Foundation Kit sur lesquelles l' interface utilisateur NeXTSTEP et Interface Builder étaient basés. Alors que les postes de travail NeXT n'ont pas eu un grand impact sur le marché, les outils ont été largement salués dans l'industrie. Cela a conduit NeXT à abandonner la production de matériel et à se concentrer sur les outils logiciels, en vendant NeXTSTEP (et OPENSTEP) en tant que plate-forme de programmation personnalisée.

Afin de contourner les termes de la GPL , NeXT avait initialement prévu d'expédier le frontend Objective-C séparément, permettant à l'utilisateur de le lier à GCC pour produire l'exécutable du compilateur. Bien qu'initialement accepté par Richard M. Stallman , ce plan a été rejeté après que Stallman ait consulté les avocats de GNU et que NeXT ait accepté d'intégrer Objective-C à GCC.

Le travail pour étendre GCC a été dirigé par Steve Naroff, qui a rejoint NeXT de StepStone. Les modifications du compilateur ont été rendues disponibles selon les termes de la licence GPL , mais pas les bibliothèques d'exécution, rendant la contribution open source inutilisable pour le grand public. Cela a conduit d'autres parties à développer de telles bibliothèques d'exécution sous licence open source. Plus tard, Steve Naroff a également été le principal contributeur au travail chez Apple pour construire l'interface Objective-C de Clang .

Le projet GNU a commencé à travailler sur son implémentation logicielle libre de Cocoa , nommée GNUstep , basée sur la norme OpenStep . Dennis Glatting a écrit le premier runtime GNU Objective-C en 1992. Le runtime GNU Objective-C, utilisé depuis 1993, est celui développé par Kresten Krab Thorup lorsqu'il était étudiant à l'université au Danemark . Thorup a également travaillé chez NeXT de 1993 à 1996.

Développement Apple et Swift

Après avoir acquis NeXT en 1996, Apple Computer a utilisé OpenStep dans son nouveau système d'exploitation, Mac OS X . Cela comprenait Objective-C, l'outil de développement basé sur Objective-C de NeXT, Project Builder , et son outil de conception d'interface, Interface Builder . Les deux ont ensuite été fusionnés en une seule application, Xcode . La plupart des API Cocoa actuelles d'Apple sont basées sur des objets d'interface OpenStep et constituent l'environnement Objective-C le plus important utilisé pour le développement actif.

Lors de la WWDC 2014, Apple a introduit un nouveau langage, Swift , qui a été qualifié d'"Objective-C without the C".

Syntaxe

Objective-C est une couche mince au-dessus de C et est un " super-ensemble strict " de C, ce qui signifie qu'il est possible de compiler n'importe quel programme C avec un compilateur Objective-C et d'inclure librement du code en langage C dans une classe Objective-C.

Objective-C dérive sa syntaxe objet de Smalltalk . Toute la syntaxe des opérations non orientées objet (y compris les variables primitives, le prétraitement, les expressions, les déclarations de fonction et les appels de fonction) est identique à celles du C, tandis que la syntaxe des fonctionnalités orientées objet est une implémentation de Smalltalk- messagerie de style.

messages

Le modèle Objective-C de programmation orientée objet est basé sur la transmission de messages aux instances d'objets. En Objective-C on n'appelle pas de méthode ; on envoie un message . Ceci est différent du modèle de programmation de style Simula utilisé par C++ . La différence entre ces deux concepts réside dans la manière dont le code référencé par la méthode ou le nom du message est exécuté. Dans un langage de style Simula, le nom de la méthode est dans la plupart des cas lié à une section de code dans la classe cible par le compilateur. En Smalltalk et Objective-C, la cible d'un message est résolue au moment de l'exécution, l'objet destinataire interprétant lui-même le message. Une méthode est identifiée par un sélecteur ou SEL — un identifiant unique pour chaque nom de message, souvent juste une chaîne terminée par NUL représentant son nom — et résolue en un pointeur de méthode C l' implémentant : un IMP . Une conséquence de ceci est que le système de transmission de messages n'a pas de vérification de type. L'objet vers lequel le message est dirigé — le récepteur — n'est pas assuré de répondre à un message, et s'il ne le fait pas, il lève une exception.

L'envoi de la méthode message à l'objet pointé par le pointeur obj nécessiterait le code suivant en C++ :

obj->method(argument);

En Objective-C, cela s'écrit comme suit :

[obj method:argument];

L'appel "method" est traduit par le compilateur dans la famille de fonctions d'exécution objc_msgSend(id self, SEL op, ...) . Différentes implémentations gèrent les ajouts modernes comme super . Dans les familles GNU, cette fonction est nommée objc_msg_sendv , mais elle a été dépréciée en faveur d'un système de recherche moderne sous objc_msg_lookup .

Les deux styles de programmation ont leurs forces et leurs faiblesses. La programmation orientée objet dans le style Simula ( C++ ) permet un héritage multiple et une exécution plus rapide en utilisant la liaison au moment de la compilation chaque fois que possible, mais elle ne prend pas en charge la liaison dynamique par défaut. Cela force également toutes les méthodes à avoir une implémentation correspondante à moins qu'elles ne soient abstraites . La programmation de style Smalltalk telle qu'elle est utilisée dans Objective-C permet aux messages de ne pas être implémentés, la méthode étant résolue à son implémentation au moment de l'exécution. Par exemple, un message peut être envoyé à une collection d'objets, auxquels seuls certains seront censés répondre, sans craindre de produire des erreurs d'exécution. La transmission de messages ne nécessite pas non plus qu'un objet soit défini au moment de la compilation. Une implémentation est toujours requise pour que la méthode soit appelée dans l'objet dérivé. (Voir la section de saisie dynamique ci-dessous pour plus d'avantages de la liaison dynamique (tardive).)

Interfaces et implémentations

Objective-C exige que l'interface et l'implémentation d'une classe soient dans des blocs de code déclarés séparément. Par convention, les développeurs placent l'interface dans un fichier d' en- tête et l'implémentation dans un fichier de code. Les fichiers d'en-tête, normalement suffixés .h, sont similaires aux fichiers d'en-tête C tandis que les fichiers d'implémentation (méthode), normalement suffixés .m, peuvent être très similaires aux fichiers de code C.

Interface

Ceci est analogue aux déclarations de classe utilisées dans d'autres langages orientés objet, tels que C++ ou Python.

L'interface d'une classe est généralement définie dans un fichier d'en-tête. Une convention courante consiste à nommer le fichier d'en-tête d'après le nom de la classe, par exemple Ball.h contiendrait l'interface de la classe Ball .

Une déclaration d'interface prend la forme :

@interface classname : superclassname {
  // instance variables
}
+ classMethod1;
+ (return_type)classMethod2;
+ (return_type)classMethod3:(param1_type)param1_varName;

- (return_type)instanceMethod1With1Parameter:(param1_type)param1_varName;
- (return_type)instanceMethod2With2Parameters:(param1_type)param1_varName
                              param2_callName:(param2_type)param2_varName;
@end

Dans ce qui précède, les signes plus désignent les méthodes de classe , ou les méthodes qui peuvent être appelées sur la classe elle-même (pas sur une instance), et les signes moins désignent les méthodes d'instance , qui ne peuvent être appelées que sur une instance particulière de la classe. Les méthodes de classe n'ont pas non plus accès aux variables d'instance .

Le code ci-dessus est à peu près équivalent à l' interface C++ suivante :

class classname : public superclassname {
protected:
  // instance variables

public:
  // Class (static) functions
  static void *classMethod1();
  static return_type classMethod2();
  static return_type classMethod3(param1_type param1_varName);

  // Instance (member) functions
  return_type instanceMethod1With1Parameter(param1_type param1_varName);
  return_type
  instanceMethod2With2Parameters(param1_type param1_varName,
                                 param2_type param2_varName = default);
};

Notez que instanceMethod2With2Parameters:param2_callName : illustre l'entrelacement de segments de sélecteur avec des expressions d'argument, pour lesquelles il n'y a pas d'équivalent direct en C/C++.

Les types de retour peuvent être n'importe quel type C standard , un pointeur vers un objet Objective-C générique, un pointeur vers un type d'objet spécifique tel que NSArray *, NSImage * ou NSString *, ou un pointeur vers la classe à laquelle appartient la méthode (type d'instance). Le type de retour par défaut est le type générique Objective-C id .

Les arguments de méthode commencent par un nom étiquetant l'argument qui fait partie du nom de la méthode, suivi de deux-points suivi du type d'argument attendu entre parenthèses et du nom de l'argument. L'étiquette peut être omise.

- (void)setRangeStart:(int)start end:(int)end;
- (void)importDocumentWithName:(NSString *)name
      withSpecifiedPreferences:(Preferences *)prefs
                    beforePage:(int)insertPage;

Un dérivé de la définition d'interface est la catégorie , qui permet d'ajouter des méthodes aux classes existantes.

Mise en œuvre

L'interface ne déclare que l'interface de classe et non les méthodes elles-mêmes : le code réel est écrit dans le fichier d'implémentation. Les fichiers de mise en œuvre (méthode) ont normalement l'extension de fichier .m, qui signifiait à l'origine "messages".

@implementation classname
+ (return_type)classMethod {
  // implementation
}
- (return_type)instanceMethod {
  // implementation
}
@end

Les méthodes sont écrites à l'aide de leurs déclarations d'interface. Comparer Objective-C et C :

- (int)method:(int)i {
  return [self square_root:i];
}
int function(int i) {
  return square_root(i);
}

La syntaxe permet de pseudo- nommer les arguments .

- (void)changeColorToRed:(float)red green:(float)green blue:(float)blue {
  //... Implementation ...
}

// Called like so:
[myColor changeColorToRed:5.0 green:2.0 blue:6.0];

Les représentations internes d'une méthode varient selon les différentes implémentations d'Objective-C. Si myColor est de la classe Color , la méthode d'instance -changeColorToRed:green:blue: peut être étiquetée en interne _i_Color_changeColorToRed_green_blue . Le i fait référence à une méthode d'instance, avec les noms de classe, puis de méthode ajoutés et les deux-points remplacés par des traits de soulignement. Comme l'ordre des paramètres fait partie du nom de la méthode, il ne peut pas être modifié pour s'adapter au style de codage ou à l'expression comme avec les vrais paramètres nommés.

Cependant, les noms internes de la fonction sont rarement utilisés directement. Généralement, les messages sont convertis en appels de fonction définis dans la bibliothèque d'exécution Objective-C. On ne sait pas nécessairement au moment de la liaison quelle méthode sera appelée car la classe du récepteur (l'objet auquel le message est envoyé) n'a pas besoin d'être connue avant l'exécution.

Instanciation

Une fois qu'une classe Objective-C est écrite, elle peut être instanciée. Cela se fait en allouant d'abord une instance non initialisée de la classe (un objet), puis en l'initialisant. Un objet n'est pas entièrement fonctionnel tant que les deux étapes ne sont pas terminées. Ces étapes doivent être accomplies avec une seule ligne de code afin qu'il n'y ait jamais un objet alloué qui n'ait pas subi d'initialisation (et parce qu'il n'est pas judicieux de conserver le résultat intermédiaire puisqu'il -initpeut renvoyer un objet différent de celui sur lequel il est appelé).

Instanciation avec l'initialiseur par défaut sans paramètre :

MyObject *foo = [[MyObject alloc] init];

Instanciation avec un initialiseur personnalisé :

MyObject *foo = [[MyObject alloc] initWithString:myString];

Dans le cas où aucune initialisation personnalisée n'est effectuée, la méthode "new" peut souvent être utilisée à la place des messages alloc-init :

MyObject *foo = [MyObject new];

De plus, certaines classes implémentent des initialiseurs de méthode de classe. Comme +new, ils combinent +allocet -init, mais contrairement à +new, ils renvoient une instance autopubliée. Certains initialiseurs de méthodes de classe prennent des paramètres :

MyObject *foo = [MyObject object];
MyObject *bar = [MyObject objectWithString:@"Wikipedia :)"];

Le message alloc alloue suffisamment de mémoire pour contenir toutes les variables d'instance d'un objet, définit toutes les variables d'instance sur des valeurs nulles et transforme la mémoire en une instance de la classe ; à aucun moment au cours de l'initialisation, la mémoire n'est une instance de la superclasse.

Le message d' initialisation effectue la configuration de l'instance lors de sa création. La méthode init s'écrit souvent comme suit :

- (id)init {
    self = [super init];
    if (self) {
        // perform initialization of object here
    }
    return self;
}

Dans l'exemple ci-dessus, notez le idtype de retour. Ce type signifie "pointeur vers n'importe quel objet" en Objective-C (Voir la section Typage dynamique ).

Le modèle d'initialisation est utilisé pour s'assurer que l'objet est correctement initialisé par sa superclasse avant que la méthode init n'effectue son initialisation. Il effectue les actions suivantes :

  1. soi = [super init]
    Envoie à l'instance de superclasse un message d' initialisation et attribue le résultat à self (pointeur vers l'objet courant).
  2. si (soi-même)
    Vérifie si le pointeur d'objet renvoyé est valide avant d'effectuer toute initialisation.
  3. retourner soi-même
    Renvoie la valeur de self à l'appelant.

Un pointeur d'objet non valide a la valeur nil ; Les instructions conditionnelles telles que "if" traitent nil comme un pointeur nul, donc le code d'initialisation ne sera pas exécuté si [super init] renvoie nil. S'il y a une erreur lors de l'initialisation, la méthode init doit effectuer tout nettoyage nécessaire, y compris l'envoi d'un message "release" à self, et renvoyer nil pour indiquer que l'initialisation a échoué. Toute vérification de telles erreurs ne doit être effectuée qu'après avoir appelé l'initialisation de la superclasse pour s'assurer que la destruction de l'objet se fera correctement.

Si une classe a plus d'une méthode d'initialisation, une seule d'entre elles (l'« initialiseur désigné ») doit suivre ce modèle ; d'autres devraient appeler l'initialiseur désigné au lieu de l'initialiseur de la superclasse.

Protocoles

Dans d'autres langages de programmation, ceux-ci sont appelés "interfaces".

Objective-C a été étendu à NeXT pour introduire le concept d' héritage multiple de spécification, mais pas d'implémentation, via l'introduction de protocoles . Il s'agit d'un modèle réalisable soit en tant que classe de base héritée multiple abstraite en C++ , soit en tant qu'"interface" (comme en Java et C# ). Objective-C utilise des protocoles ad hoc appelés protocoles informels et des protocoles imposés par le compilateur appelés protocoles formels .

Un protocole informel est une liste de méthodes qu'une classe peut choisir d'implémenter. Il est précisé dans la documentation, puisqu'il n'est pas présent dans la langue. Les protocoles informels sont implémentés en tant que catégorie (voir ci-dessous) sur NSObject et incluent souvent des méthodes optionnelles, qui, si elles sont implémentées, peuvent changer le comportement d'une classe. Par exemple, une classe de champ de texte peut avoir un délégué qui implémente un protocole informel avec une méthode facultative pour effectuer la saisie semi-automatique du texte saisi par l'utilisateur. Le champ de texte découvre si le délégué implémente cette méthode (via réflexion ) et, si tel est le cas, appelle la méthode du délégué pour prendre en charge la fonctionnalité de saisie semi-automatique.

Un protocole formel est similaire à une interface en Java, C# et Ada 2005 . C'est une liste de méthodes que n'importe quelle classe peut déclarer implémenter. Les versions d'Objective-C antérieures à 2.0 exigeaient qu'une classe implémente toutes les méthodes dans un protocole qu'elle déclare adopter ; le compilateur émettra une erreur si la classe n'implémente pas toutes les méthodes de ses protocoles déclarés. Objective-C 2.0 a ajouté la prise en charge du marquage facultatif de certaines méthodes dans un protocole, et le compilateur n'appliquera pas l'implémentation de méthodes facultatives.

Une classe doit être déclarée pour implémenter ce protocole pour être déclarée conforme. Ceci est détectable au moment de l'exécution. Les protocoles formels ne peuvent fournir aucune implémentation ; ils assurent simplement aux appelants que les classes conformes au protocole fourniront des implémentations. Dans la bibliothèque NeXT/Apple, les protocoles sont fréquemment utilisés par le système d'objets distribués pour représenter les capacités d'un objet s'exécutant sur un système distant.

La syntaxe

@protocol NSLocking
- (void)lock;
- (void)unlock;
@end

indique qu'il y a l'idée abstraite de verrouillage. En indiquant dans la définition de classe que le protocole est implémenté,

@interface NSLock : NSObject <NSLocking>
// ...
@end

les instances de NSLock prétendent qu'elles fourniront une implémentation pour les deux méthodes d'instance.

Saisie dynamique

Objective-C, comme Smalltalk, peut utiliser le typage dynamique : un objet peut recevoir un message qui n'est pas spécifié dans son interface. Cela peut permettre une flexibilité accrue, car cela permet à un objet de « capturer » un message et d'envoyer le message à un autre objet qui peut répondre au message de manière appropriée, ou de même envoyer le message à un autre objet. Ce comportement est connu sous le nom de transfert ou délégation de messages (voir ci-dessous). Alternativement, un gestionnaire d'erreurs peut être utilisé au cas où le message ne peut pas être transféré. Si un objet ne transmet pas de message, n'y répond pas ou ne gère pas une erreur, le système génère une exception d'exécution. Si des messages sont envoyés à nil (le pointeur d'objet null), ils seront ignorés silencieusement ou déclencheront une exception générique, selon les options du compilateur.

Des informations de typage statique peuvent également être facultativement ajoutées aux variables. Ces informations sont ensuite vérifiées au moment de la compilation. Dans les quatre instructions suivantes, des informations de type de plus en plus spécifiques sont fournies. Les instructions sont équivalentes à l'exécution, mais les informations supplémentaires permettent au compilateur d'avertir le programmeur si l'argument passé ne correspond pas au type spécifié.

- (void)setMyValue:(id)foo;

Dans la déclaration ci-dessus, foo peut appartenir à n'importe quelle classe.

- (void)setMyValue:(id<NSCopying>)foo;

Dans la déclaration ci-dessus, foo peut être une instance de n'importe quelle classe conforme au NSCopyingprotocole.

- (void)setMyValue:(NSNumber *)foo;

Dans l'instruction ci-dessus, foo doit être une instance de la classe NSNumber .

- (void)setMyValue:(NSNumber<NSCopying> *)foo;

Dans l'instruction ci-dessus, foo doit être une instance de la classe NSNumber et doit être conforme au NSCopyingprotocole.

En Objective-C, tous les objets sont représentés sous forme de pointeurs et l'initialisation statique n'est pas autorisée. L'objet le plus simple est le type vers lequel pointe id ( objc_obj * ), qui n'a qu'un pointeur isa décrivant sa classe. D'autres types de C, comme les valeurs et les structures, sont inchangés car ils ne font pas partie du système d'objets. Cette décision diffère du modèle objet C++, où les structures et les classes sont unies.

Expéditeur

Objective-C permet l'envoi d'un message à un objet qui peut ne pas répondre. Plutôt que de répondre ou simplement de laisser tomber le message, un objet peut transmettre le message à un objet qui peut répondre. Le transfert peut être utilisé pour simplifier la mise en œuvre de certains modèles de conception , tels que le modèle observateur ou le modèle proxy .

Le runtime Objective-C spécifie une paire de méthodes dans Object

  • méthodes de transfert :
    - (retval_t)forward:(SEL)sel args:(arglist_t)args; // with GCC
    - (id)forward:(SEL)sel args:(marg_list)args; // with NeXT/Apple systems
    
  • méthodes d'action :
    - (retval_t)performv:(SEL)sel args:(arglist_t)args; // with GCC
    - (id)performv:(SEL)sel args:(marg_list)args; // with NeXT/Apple systems
    

Un objet souhaitant implémenter la transmission n'a besoin que de remplacer la méthode de transmission par une nouvelle méthode pour définir le comportement de transmission. La méthode d'action performv :: n'a pas besoin d'être remplacée, car cette méthode effectue simplement une action basée sur le sélecteur et les arguments. Notez le SELtype, qui est le type de messages en Objective-C.

Remarque : dans OpenStep, Cocoa et GNUstep, les frameworks d'Objective-C couramment utilisés, on n'utilise pas la classe Object . La méthode - (void)forwardInvocation:(NSInvocation *)anInvocation de la classe NSObject est utilisée pour effectuer le transfert.

Exemple

Voici un exemple de programme qui montre les bases du transfert.

Transitaire.h
#import <objc/Object.h>

@interface Forwarder : Object {
  id recipient; // The object we want to forward the message to.
}

// Accessor methods.
- (id)recipient;
- (id)setRecipient:(id)_recipient;
@end
Transitaire.m
#import "Forwarder.h"

@implementation Forwarder
- (retval_t)forward:(SEL)sel args:(arglist_t)args {
  /*
  * Check whether the recipient actually responds to the message.
  * This may or may not be desirable, for example, if a recipient
  * in turn does not respond to the message, it might do forwarding
  * itself.
  */
  if ([recipient respondsToSelector:sel]) {
    return [recipient performv:sel args:args];
  } else {
    return [self error:"Recipient does not respond"];
  }
}

- (id)setRecipient:(id)_recipient {
  [recipient autorelease];
  recipient = [_recipient retain];
  return self;
}

- (id)recipient {
  return recipient;
}
@end
Destinataire.h
#import <objc/Object.h>

// A simple Recipient object.
@interface Recipient : Object
- (id)hello;
@end
Destinataire.m
#import "Recipient.h"

@implementation Recipient

- (id)hello {
  printf("Recipient says hello!\n");

  return self;
}

@end
main.m
#import "Forwarder.h"
#import "Recipient.h"

int main(void) {
  Forwarder *forwarder = [Forwarder new];
  Recipient *recipient = [Recipient new];

  [forwarder setRecipient:recipient]; // Set the recipient.
  /*
  * Observe forwarder does not respond to a hello message! It will
  * be forwarded. All unrecognized methods will be forwarded to
  * the recipient
  * (if the recipient responds to them, as written in the Forwarder)
  */
  [forwarder hello];

  [recipient release];
  [forwarder release];

  return 0;
}

Remarques

Lorsqu'il est compilé à l'aide de gcc , le compilateur signale :

$ gcc -x objective-c -Wno-import Forwarder.m Recipient.m main.m -lobjc
main.m: In function `main':
main.m:12: warning: `Forwarder' does not respond to `hello'
$

Le compilateur signale le point soulevé plus tôt, que Forwarder ne répond pas aux messages hello. Dans ce cas, il est prudent d'ignorer l'avertissement puisque le transfert a été mis en œuvre. L'exécution du programme produit cette sortie :

$ ./a.out
Recipient says hello!

Catégories

Lors de la conception d'Objective-C, l'une des principales préoccupations était la maintenabilité de grandes bases de code. L'expérience du monde de la programmation structurée avait montré que l'un des principaux moyens d'améliorer le code était de le décomposer en plus petits morceaux. Objective-C a emprunté et étendu le concept de catégories des implémentations Smalltalk pour faciliter ce processus.

De plus, les méthodes d'une catégorie sont ajoutées à une classe au moment de l' exécution . Ainsi, les catégories permettent au programmeur d'ajouter des méthodes à une classe existante - une classe ouverte - sans avoir besoin de recompiler cette classe ou même d'avoir accès à son code source. Par exemple, si un système ne contient pas de correcteur orthographique dans son implémentation String, il peut être ajouté sans modifier le code source de String.

Les méthodes au sein des catégories deviennent indiscernables des méthodes d'une classe lorsque le programme est exécuté. Une catégorie a un accès complet à toutes les variables d'instance au sein de la classe, y compris les variables privées.

Si une catégorie déclare une méthode avec la même signature de méthode qu'une méthode existante dans une classe, la méthode de la catégorie est adoptée. Ainsi, les catégories peuvent non seulement ajouter des méthodes à une classe, mais aussi remplacer des méthodes existantes. Cette fonctionnalité peut être utilisée pour corriger des bogues dans d'autres classes en réécrivant leurs méthodes, ou pour provoquer un changement global du comportement d'une classe dans un programme. Si deux catégories ont des méthodes avec le même nom mais des signatures de méthode différentes, il n'est pas défini quelle méthode de catégorie est adoptée.

D'autres langues ont tenté d'ajouter cette fonctionnalité de diverses manières. TOM a poussé le système Objective-C un peu plus loin et a également permis l'ajout de variables. D'autres langages ont plutôt utilisé des solutions basées sur des prototypes , le plus notable étant Self .

Les langages C# et Visual Basic.NET implémentent des fonctionnalités superficiellement similaires sous la forme de méthodes d'extension , mais celles-ci n'ont pas accès aux variables privées de la classe. Ruby et plusieurs autres langages de programmation dynamique font référence à la technique sous le nom de " patch de singe ".

Logtalk implémente un concept de catégories (en tant qu'entités de première classe) qui englobe la fonctionnalité des catégories Objective-C (les catégories Logtalk peuvent également être utilisées comme unités de composition à grain fin lors de la définition, par exemple, de nouvelles classes ou de prototypes ; en particulier, une catégorie Logtalk peut être virtuellement importé par un nombre quelconque de classes et de prototypes).

Exemple d'utilisation de catégories

Cet exemple construit une classe Integer , en définissant d'abord une classe de base avec uniquement des méthodes d'accès implémentées, et en ajoutant deux catégories, Arithmetic et Display , qui étendent la classe de base. Bien que les catégories puissent accéder aux membres de données privées de la classe de base, il est souvent recommandé d'accéder à ces membres de données privées via les méthodes d'accès, ce qui permet de garder les catégories plus indépendantes de la classe de base. La mise en œuvre de tels accesseurs est une utilisation typique des catégories. Une autre consiste à utiliser des catégories pour ajouter des méthodes à la classe de base. Cependant, il n'est pas considéré comme une bonne pratique d'utiliser des catégories pour le remplacement de sous-classes, également connu sous le nom de patch de singe . Les protocoles informels sont implémentés en tant que catégorie sur la classe de base NSObject . Par convention, les fichiers contenant des catégories qui étendent les classes de base prendront le nom BaseClass+ExtensionClass.h .

Entier.h
#import <objc/Object.h>

@interface Integer : Object {
  int integer;
}

- (int)integer;
- (id)integer:(int)_integer;
@end
Entier.m
#import "Integer.h"

@implementation Integer
- (int) integer {
  return integer;
}

- (id) integer: (int) _integer {
  integer = _integer;
  return self;
}
@end
Entier+arithmétique.h
#import "Integer.h"

@interface Integer (Arithmetic)
- (id) add: (Integer *) addend;
- (id) sub: (Integer *) subtrahend;
@end
Entier+arithmétique.m
# import "Integer+Arithmetic.h"

@implementation Integer (Arithmetic)
- (id) add: (Integer *) addend {
  return [self integer: [self integer] + [addend integer]];
}

- (id) sub: (Integer *) subtrahend {
  return [self integer: [self integer] - [subtrahend integer]];
}
@end
Entier+Affichage.h
#import "Integer.h"

@interface Integer (Display)
- (id) showstars;
- (id) showint;
@end
Entier+Affichage.m
# import "Integer+Display.h"

@implementation Integer (Display)
- (id) showstars {
  int i, x = [self integer];
  for (i = 0; i < x; i++) {
    printf("*");
  }
  printf("\n");

  return self;
}

- (id) showint {
  printf("%d\n", [self integer]);

  return self;
}
@end
main.m
#import "Integer.h"
#import "Integer+Arithmetic.h"
#import "Integer+Display.h"

int main(void) {
  Integer *num1 = [Integer new], *num2 = [Integer new];
  int x;

  printf("Enter an integer: ");
  scanf("%d", &x);

  [num1 integer:x];
  [num1 showstars];

  printf("Enter an integer: ");
  scanf("%d", &x);

  [num2 integer:x];
  [num2 showstars];

  [num1 add:num2];
  [num1 showint];

  return 0;
}

Remarques

La compilation est effectuée, par exemple, par :

gcc -x objective-c main.m Integer.m Integer+Arithmetic.m Integer+Display.m -lobjc

On peut expérimenter en omettant les lignes #import "Integer+Arithmetic.h" et [num1 add:num2] et en omettant Integer+Arithmetic.m dans la compilation. Le programme continuera de fonctionner. Cela signifie qu'il est possible de mélanger et assortir les catégories ajoutées si nécessaire ; si une catégorie n'a pas besoin d'avoir une certaine capacité, elle ne peut tout simplement pas être compilée.

Pose

Objective-C permet à une classe de remplacer entièrement une autre classe dans un programme. On dit que la classe de remplacement "se pose comme" la classe cible.

La pose en classe a été déclarée obsolète avec Mac OS X v10.5 et n'est pas disponible dans l'environnement d'exécution 64 bits. Des fonctionnalités similaires peuvent être obtenues en utilisant des méthodes qui sillonnent les catégories, qui échange l'implémentation d'une méthode avec une autre qui a la même signature.

Pour les versions prenant toujours en charge le posing, tous les messages envoyés à la classe cible sont plutôt reçus par la classe posing. Il existe plusieurs restrictions :

  • Une classe ne peut se poser que comme l'une de ses superclasses directes ou indirectes.
  • La classe posante ne doit pas définir de nouvelles variables d'instance absentes de la classe cible (bien qu'elle puisse définir ou remplacer des méthodes).
  • La classe cible peut n'avoir reçu aucun message avant la pose.

La pose, de la même manière que les catégories, permet une augmentation globale des classes existantes. La pose permet deux caractéristiques absentes des catégories :

  • Une classe posante peut appeler des méthodes surchargées via super, incorporant ainsi l'implémentation de la classe cible.
  • Une classe de pose peut remplacer les méthodes définies dans les catégories.

Par exemple,

@interface CustomNSApplication : NSApplication
@end

@implementation CustomNSApplication
- (void) setMainMenu: (NSMenu*) menu {
  // do something with menu
}
@end

class_poseAs ([CustomNSApplication class], [NSApplication class]);

Cela intercepte chaque invocation de setMainMenu à NSApplication.

#importer

Dans le langage C, la #includedirective de pré-compilation provoque toujours l'insertion du contenu d'un fichier dans la source à ce stade. Objective-C a la #importdirective, équivalente sauf que chaque fichier n'est inclus qu'une seule fois par unité de compilation, évitant ainsi le besoin d' inclure des gardes .

Compilation Linux gcc

// FILE: hello.m
#import <Foundation/Foundation.h>
int main (int argc, const char * argv[])
{
    /* my first program in Objective-C */
    NSLog(@"Hello, World! \n");
    return 0;
}
# Compile Command Line for gcc and MinGW Compiler:
$ gcc \
    $(gnustep-config --objc-flags) \
    -o hello \
    hello.m \
    -L /GNUstep/System/Library/Libraries \
    -lobjc \
    -lgnustep-base

$ ./hello

Autres caractéristiques

Les fonctionnalités d'Objective-C permettent souvent des solutions flexibles et souvent simples aux problèmes de programmation.

  • La délégation de méthodes à d'autres objets et l'invocation à distance peuvent être facilement implémentées à l'aide de catégories et de transfert de messages.
  • Le tourbillonnement du pointeur isa permet aux classes de changer au moment de l'exécution. Généralement utilisé pour le débogage où les objets libérés sont transformés en objets zombies dont le seul but est de signaler une erreur lorsque quelqu'un les appelle. Swizzling a également été utilisé dans Enterprise Objects Framework pour créer des défauts de base de données. Swizzling est utilisé aujourd'hui par le framework Foundation d'Apple pour implémenter l' observation des valeurs-clés .

Variantes linguistiques

Objective-C++

Objective-C++ est une variante de langage acceptée par le front-end de GNU Compiler Collection et Clang , qui peut compiler des fichiers source utilisant une combinaison de syntaxe C++ et Objective-C. Objective-C++ ajoute au C++ les extensions qu'Objective-C ajoute au C. Comme rien n'est fait pour unifier la sémantique derrière les différentes fonctionnalités du langage, certaines restrictions s'appliquent :

  • Une classe C++ ne peut pas dériver d'une classe Objective-C et vice versa.
  • Les espaces de noms C++ ne peuvent pas être déclarés dans une déclaration Objective-C.
  • Les déclarations Objective-C peuvent apparaître uniquement dans la portée globale, pas dans un espace de noms C++
  • Les classes Objective-C ne peuvent pas avoir de variables d'instance de classes C++ qui n'ont pas de constructeur par défaut ou qui ont une ou plusieurs méthodes virtuelles , mais les pointeurs vers des objets C++ peuvent être utilisés comme variables d'instance sans restriction (allouez-les avec new dans la méthode -init).
  • La sémantique C++ "par valeur" ne peut pas être appliquée aux objets Objective-C, qui ne sont accessibles que par des pointeurs.
  • Une déclaration Objective-C ne peut pas être dans une déclaration de modèle C++ et vice versa. Cependant, les types Objective-C (par exemple, Classname *) peuvent être utilisés comme paramètres de modèle C++.
  • La gestion des exceptions Objective-C et C++ est distincte ; les gestionnaires de chacun ne peuvent pas gérer les exceptions de l'autre type. Par conséquent, les destructeurs d'objets ne sont pas exécutés. Ceci est atténué dans les récents runtimes "Objective-C 2.0" car les exceptions Objective-C sont soit complètement remplacées par des exceptions C++ (exécution Apple), soit partiellement lorsque la bibliothèque Objective-C++ est liée (GNUstep libobjc2).
  • Les blocs Objective-C et les lambdas C++11 sont des entités distinctes. Cependant, un bloc est généré de manière transparente sur macOS lors du passage d'un lambda où un bloc est attendu.

Objectif-C 2.0

Lors de la conférence mondiale des développeurs 2006 , Apple a annoncé la sortie de "Objective-C 2.0", une révision du langage Objective-C pour inclure "la récupération de place moderne, des améliorations de la syntaxe, des améliorations des performances d'exécution et une prise en charge 64 bits". Mac OS X v10.5 , sorti en octobre 2007, incluait un compilateur Objective-C 2.0. GCC 4.6 prend en charge de nombreuses nouvelles fonctionnalités Objective-C, telles que les propriétés déclarées et synthétisées, la syntaxe à points, l'énumération rapide, les méthodes de protocole facultatives, les attributs de méthode/protocole/classe, les extensions de classe et une nouvelle API d'exécution GNU Objective-C.

Le nom Objective-C 2.0 représente une rupture dans le système de versionnage du langage, car la dernière version Objective-C pour NeXT était "objc4". Ce nom de projet a été conservé dans la dernière version du code source d'exécution Objective-C hérité dans Mac OS X Leopard (10.5).

Collecte des ordures

Objective-C 2.0 fournissait un ramasse-miettes générationnel et conservateur en option . Lorsqu'il est exécuté en mode rétrocompatible , le runtime a transformé les opérations de comptage de références telles que "retain" et "release" en no-ops . Tous les objets étaient soumis à la récupération de place lorsque la récupération de place était activée. Les pointeurs C réguliers pourraient être qualifiés avec "__strong" pour également déclencher les interceptions sous-jacentes du compilateur de la barrière d'écriture et ainsi participer au ramasse-miettes. Un sous-système faible de mise à zéro a également été fourni de telle sorte que les pointeurs marqués comme "__weak" soient mis à zéro lorsque l'objet (ou plus simplement, la mémoire GC) est collecté. Le ramasse-miettes n'existe pas sur l'implémentation iOS d'Objective-C 2.0. La récupération de place dans Objective-C s'exécute sur un thread d'arrière-plan de faible priorité et peut s'arrêter sur les événements utilisateur, dans le but de maintenir l'expérience utilisateur réactive.

La collecte des ordures a été dépréciée dans Mac OS X v10.8 en faveur du comptage automatique de références (ARC). Objective-C sur iOS 7 fonctionnant sur ARM64 utilise 19 bits sur un mot de 64 bits pour stocker le nombre de références, sous forme de pointeurs étiquetés .

Propriétés

Objective-C 2.0 introduit une nouvelle syntaxe pour déclarer les variables d'instance en tant que propriétés , avec des attributs facultatifs pour configurer la génération des méthodes d'accès. Les propriétés sont, dans un sens, des variables d'instance publiques ; c'est-à-dire que déclarer une variable d'instance en tant que propriété fournit aux classes externes un accès (éventuellement limité, par exemple en lecture seule) à cette propriété. Une propriété peut être déclarée en "lecture seule" et peut être dotée d'une sémantique de stockage telle que assign, copyou retain. Par défaut, les propriétés sont prises en compte atomic, ce qui entraîne un verrou empêchant plusieurs threads d'y accéder en même temps. Une propriété peut être déclarée comme nonatomic, ce qui supprime ce verrou.

@interface Person : NSObject {
@public
  NSString *name;
@private
  int age;
}

@property(copy) NSString *name;
@property(readonly) int age;

- (id)initWithAge:(int)age;
@end

Les propriétés sont implémentées au moyen du @synthesizemot - clé, qui génère des méthodes getter (et setter, sinon en lecture seule) en fonction de la déclaration de propriété. Alternativement, les méthodes getter et setter doivent être implémentées explicitement, ou le @dynamicmot - clé peut être utilisé pour indiquer que les méthodes d'accès seront fournies par d'autres moyens. Lorsqu'elles sont compilées à l'aide de clang 3.1 ou supérieur, toutes les propriétés qui ne sont pas explicitement déclarées avec @dynamic, marquées readonlyou qui ont un getter et un setter complets implémentées par l'utilisateur seront automatiquement implicitement @synthesize'd.

@implementation Person
@synthesize name;

- (id)initWithAge:(int)initAge {
  self = [super init];
  if (self) {
    // NOTE: direct instance variable assignment, not property setter
    age = initAge;
  }
  return self;
}

- (int)age {
  return age;
}
@end

Les propriétés sont accessibles à l'aide de la syntaxe de passage de message traditionnelle, de la notation par points ou, dans le codage clé-valeur, par nom via les méthodes "valueForKey:"/"setValue:forKey:".

Person *aPerson = [[Person alloc] initWithAge:53];
aPerson.name = @"Steve"; // NOTE: dot notation, uses synthesized setter,
                         // equivalent to [aPerson setName: @"Steve"];
NSLog(@"Access by message (%@), dot notation(%@), property name(% @) and "
       "direct instance variable access(% @) ",
              [aPerson name],
      aPerson.name, [aPerson valueForKey:@"name"], aPerson -> name);

Afin d'utiliser la notation par points pour invoquer des accesseurs de propriété dans une méthode d'instance, le mot-clé "self" doit être utilisé :

- (void)introduceMyselfWithProperties:(BOOL)useGetter {
  NSLog(@"Hi, my name is %@.", (useGetter ? self.name : name));
  // NOTE: getter vs. ivar access
}

Les propriétés d'une classe ou d'un protocole peuvent être introspectées dynamiquement .

int i;
int propertyCount = 0;
objc_property_t *propertyList =
    class_copyPropertyList([aPerson class], &propertyCount);

for (i = 0; i < propertyCount; i++) {
  objc_property_t *thisProperty = propertyList + i;
  const char *propertyName = property_getName(*thisProperty);
  NSLog(@"Person has a property: '%s'", propertyName);
}

Variables d'instance non fragiles

Objective-C 2.0 fournit des variables d'instance non fragiles lorsqu'elles sont prises en charge par l'environnement d'exécution (c'est-à-dire lors de la création de code pour macOS 64 bits et tous les iOS). Sous l'environnement d'exécution moderne, une couche supplémentaire d'indirection est ajoutée à l'accès aux variables d'instance, permettant à l'éditeur de liens dynamique d'ajuster la disposition de l'instance au moment de l'exécution. Cette fonctionnalité permet deux améliorations importantes du code Objective-C :

  • Il élimine le problème d'interface binaire fragile ; les superclasses peuvent changer de taille sans affecter la compatibilité binaire.
  • Il permet aux variables d'instance qui fournissent le support des propriétés d'être synthétisées au moment de l'exécution sans qu'elles soient déclarées dans l'interface de la classe.

Dénombrement rapide

Au lieu d'utiliser un objet ou des indices NSEnumerator pour parcourir une collection, Objective-C 2.0 offre la syntaxe d'énumération rapide. Dans Objective-C 2.0, les boucles suivantes sont fonctionnellement équivalentes, mais ont des caractéristiques de performances différentes.

// Using NSEnumerator
NSEnumerator *enumerator = [thePeople objectEnumerator];
Person *p;

while ((p = [enumerator nextObject]) != nil) {
  NSLog(@"%@ is %i years old.", [p name], [p age]);
}
// Using indexes
for (int i = 0; i < [thePeople count]; i++) {
  Person *p = [thePeople objectAtIndex:i];
  NSLog(@"%@ is %i years old.", [p name], [p age]);
}
// Using fast enumeration
for (Person *p in thePeople) {
  NSLog(@"%@ is %i years old.", [p name], [p age]);
}

L'énumération rapide génère un code plus efficace que l'énumération standard car les appels de méthode pour énumérer les objets sont remplacés par l'arithmétique de pointeur utilisant le protocole NSFastEnumeration.

Extensions de classe

Une extension de classe a la même syntaxe qu'une déclaration de catégorie sans nom de catégorie, et les méthodes et propriétés qui y sont déclarées sont ajoutées directement à la classe principale. Il est principalement utilisé comme alternative à une catégorie pour ajouter des méthodes à une classe sans les publier dans les en-têtes publics, avec l'avantage que pour les extensions de classe, le compilateur vérifie que toutes les méthodes déclarées privées sont réellement implémentées.

Implications pour le développement du cacao

Toutes les applications Objective-C développées pour macOS qui utilisent les améliorations ci-dessus pour Objective-C 2.0 sont incompatibles avec tous les systèmes d'exploitation antérieurs à 10.5 (Leopard). Étant donné que l'énumération rapide ne génère pas exactement les mêmes binaires que l'énumération standard, son utilisation entraînera le blocage d'une application sur Mac OS X version 10.4 ou antérieure.

Blocs

Blocks est une extension non standard pour Objective-C (et C et C++ ) qui utilise une syntaxe spéciale pour créer des fermetures . Les blocs ne sont pris en charge que dans Mac OS X 10.6 "Snow Leopard" ou version ultérieure, iOS 4 ou version ultérieure, et GNUstep avec libobjc2 1.7 et compilation avec clang 3.1 ou version ultérieure.

#include <stdio.h>
#include <Block.h>
typedef int (^IntBlock)();

IntBlock MakeCounter(int start, int increment) {
  __block int i = start;

  return Block_copy( ^ {
    int ret = i;
    i += increment;
    return ret;
  });

}

int main(void) {
  IntBlock mycounter = MakeCounter(5, 2);
  printf("First call: %d\n", mycounter());
  printf("Second call: %d\n", mycounter());
  printf("Third call: %d\n", mycounter());

  /* because it was copied, it must also be released */
  Block_release(mycounter);

  return 0;
}
/* Output:
  First call: 5
  Second call: 7
  Third call: 9
*/

Objectif-C moderne

Apple a ajouté quelques fonctionnalités supplémentaires à Objective 2.0 au fil du temps. Les ajouts ne s'appliquent qu'au " compilateur Apple LLVM ", c'est-à-dire à l'interface clang du langage. De manière confuse, le versioning utilisé par Apple diffère de celui du LLVM en amont ; reportez-vous aux versions Xcode § Toolchain pour une traduction vers les numéros de version LLVM open source.

Comptage de référence automatique

Le comptage automatique de références (ARC) est une fonctionnalité au moment de la compilation qui élimine le besoin pour les programmeurs de gérer manuellement les comptes de rétention à l'aide de retainet release. Contrairement au ramasse-miettes , qui se produit au moment de l'exécution, ARC élimine la surcharge d'un processus distinct gérant les décomptes de conservation. L'ARC et la gestion manuelle de la mémoire ne s'excluent pas mutuellement ; les programmeurs peuvent continuer à utiliser du code non ARC dans les projets compatibles ARC en désactivant ARC pour les fichiers de code individuels. Xcode peut également tenter de mettre à niveau automatiquement un projet vers ARC.

ARC a été introduit dans LLVM 3.0. Cela se traduit par Xcode 4.2 (2011) ou le compilateur Apple LLVM 3.0.

Littéraux

NeXT et Apple runtimes Obj-C ont longtemps inclus un moyen à court formulaire pour créer de nouvelles chaînes, en utilisant la syntaxe littérale @"a new string", ou tomber à des constantes CoreFoundation kCFBooleanTrueet kCFBooleanFalsepour NSNumberles valeurs booléennes. L'utilisation de ce format évite au programmeur d'avoir à utiliser initWithStringdes méthodes plus longues ou similaires lors de certaines opérations.

Lors de l'utilisation du compilateur Apple LLVM 4.0 (Xcode 4.4) ou version ultérieure, des tableaux, des dictionnaires et des nombres ( NSArray, NSDictionary, NSNumberclasses) peuvent également être créés à l'aide de la syntaxe littérale au lieu de méthodes. (Le compilateur Apple LLVM 4.0 se traduit par LLVM open source et Clang 3.1.)

Exemple sans littéraux :

NSArray *myArray = [NSArray arrayWithObjects:object1,object2,object3,nil];
NSDictionary *myDictionary1 = [NSDictionary dictionaryWithObject:someObject forKey:@"key"];
NSDictionary *myDictionary2 = [NSDictionary dictionaryWithObjectsAndKeys:object1, key1, object2, key2, nil];
NSNumber *myNumber = [NSNumber numberWithInt:myInt];
NSNumber *mySumNumber= [NSNumber numberWithInt:(2 + 3)];
NSNumber *myBoolNumber = [NSNumber numberWithBool:YES];

Exemple avec des littéraux :

NSArray *myArray = @[ object1, object2, object3 ];
NSDictionary *myDictionary1 = @{ @"key" : someObject };
NSDictionary *myDictionary2 = @{ key1: object1, key2: object2 };
NSNumber *myNumber = @(myInt);
NSNumber *mySumNumber = @(2+3);
NSNumber *myBoolNumber = @YES;
NSNumber *myIntegerNumber = @8;

Cependant, à la différence des littéraux de chaîne , qui se compilent en constantes dans l'exécutable, ces littéraux se compilent en code équivalent aux appels de méthode ci-dessus. En particulier, dans le cadre d'une gestion de mémoire à comptage manuel de références, ces objets sont libérés automatiquement, ce qui nécessite des précautions supplémentaires lorsqu'ils sont, par exemple, utilisés avec des variables statiques de fonction ou d'autres types de globales.

Abonnement

Lors de l'utilisation du compilateur Apple LLVM 4.0 ou version ultérieure, les tableaux et les dictionnaires ( NSArrayet les NSDictionaryclasses) peuvent être manipulés à l'aide d'un indice. L'indice peut être utilisé pour récupérer des valeurs à partir d'index (tableau) ou de clés (dictionnaire), et avec des objets mutables, peut également être utilisé pour définir des objets sur des index ou des clés. Dans le code, l'indexation est représentée par des crochets [ ].

Exemple sans abonnement :

id object1 = [someArray objectAtIndex:0];
id object2 = [someDictionary objectForKey:@"key"];
[someMutableArray replaceObjectAtIndex:0 withObject:object3];
[someMutableDictionary setObject:object4 forKey:@"key"];

Exemple avec abonnement :

id object1 = someArray[0];
id object2 = someDictionary[@"key"];
someMutableArray[0] = object3;
someMutableDictionary[@"key"] = object4;

Syntaxe Objective-C "moderne" (1997)

Après l'achat de NeXT par Apple, des tentatives ont été faites pour rendre le langage plus acceptable pour les programmeurs plus familiers avec Java que Smalltalk. L'une de ces tentatives consistait à introduire ce qui était à l'époque surnommé la « syntaxe moderne » pour Objective-C (par opposition à la syntaxe « classique » actuelle). Il n'y a eu aucun changement de comportement, il s'agissait simplement d'une syntaxe alternative. Au lieu d'écrire une invocation de méthode comme

    object = [[MyClass alloc] init];
    [object firstLabel: param1 secondLabel: param2];

Il était plutôt écrit comme

    object = (MyClass.alloc).init;
    object.labels ( param1, param2 );

De même, les déclarations sont passées du formulaire

    -(void) firstLabel: (int)param1 secondLabel: (int)param2;

à

    -(void) labels ( int param1, int param2 );

Cette syntaxe "moderne" n'est plus supportée dans les dialectes actuels du langage Objective-C.

mulle-objc

Le projet mulle-objc est une autre ré-implémentation d'Objective-C. Il prend en charge les compilateurs GCC ou Clang / LLVM en tant que backends. Il diffère des autres environnements d'exécution en termes de syntaxe, de sémantique et de compatibilité ABI. Il prend en charge Linux, FreeBSD et Windows.

Compilateur d'objets portables

Outre l' implémentation GCC / NeXT / Apple , qui a ajouté plusieurs extensions à l' implémentation originale de Stepstone , une autre implémentation Objective-C gratuite et open source appelée Portable Object Compiler existe également. L'ensemble d'extensions implémenté par le compilateur d'objet portable diffère de l'implémentation GCC/NeXT/Apple ; en particulier, il inclut des blocs de type Smalltalk pour Objective-C, alors qu'il manque de protocoles et de catégories, deux fonctionnalités largement utilisées dans OpenStep et ses dérivés et apparentés. Dans l'ensemble, POC représente une étape plus ancienne, pré-NeXT dans l'évolution de la langue, à peu près conforme au livre de 1991 de Brad Cox.

Il comprend également une bibliothèque d'exécution appelée ObjectPak, qui est basée sur la bibliothèque ICPak101 originale de Cox (qui à son tour dérive de la bibliothèque de classes Smalltalk-80), et est assez radicalement différente de l'OpenStep FoundationKit.

Objectif GEOS-C

Le système PC GEOS utilisait un langage de programmation connu sous le nom de GEOS Objective-C ou goc ; malgré la similitude des noms, les deux langues ne sont similaires que dans le concept global et l'utilisation de mots-clés préfixés par un signe @.

Bruit

La suite de compilateurs Clang , qui fait partie du projet LLVM , implémente Objective-C et d'autres langages. Après le passage de GCC 4.3 (2008) à la GPLv3, Apple l'a abandonné au profit de clang, un compilateur qu'il a plus de pouvoir légal de modifier. Par conséquent, de nombreuses fonctionnalités du langage Objective-C moderne ne sont prises en charge que par Clang.

Le schéma de version d'Apple pour son "compilateur LLVM" basé sur clang diffère de la gestion de version open source de LLVM. Voir les versions Xcode § Toolchain pour une traduction

GNU, GNUstep et WinObjC

Le projet GNU s'intéresse depuis longtemps à une plate-forme pour porter les programmes NeXT et Obj-C. Le ChangeLog pour le répertoire libobjc dans GCC suggère qu'il existait avant 1998 (GCC 2.95), et son README indique en outre une réécriture en 1993 (GCC 2.4).

Le code source frontal de NeXT a été publié car il a été créé dans le cadre de GCC, la licence publique GNU publiée qui oblige ceux qui créent des travaux dérivés à le faire. Apple a poursuivi cette tradition en publiant son fork de GCC jusqu'à 4.2.1, après quoi ils ont abandonné le compilateur. Les responsables de GCC ont intégré les changements, mais n'ont pas beaucoup investi dans la prise en charge de nouvelles fonctionnalités telles que le langage Objective-C 2.0.

Les développeurs de GNUstep, intéressés par le nouveau langage, ont dérivé le GCC libobjc vers un projet indépendant de GCC appelé libobjc2 en 2009. Ils se sont également arrangés pour que le runtime soit utilisé avec Clang pour tirer parti de la nouvelle syntaxe du langage. GCC a évolué lentement en même temps, mais à GCC 4.6.0 (2011), ils sont également passés à Objective-C 2.0 dans leur libobjc. La documentation GNUstep suggère que l'implémentation de GCC manque toujours de support pour les blocs, les variables non fragiles et le plus récent ARC.

Microsoft fourchue libobjc2 dans une partie de WinObjC , le pont iOS pour plate - forme Windows Universal , en 2015. Combiné avec sa propre implémentation de Cocoa Touch et les API sous - jacents, le projet permet la réutilisation de code application iOS à l' intérieur des applications UWP.

Sous Windows, des outils de développement Objective-C sont fournis en téléchargement sur le site Web de GNUStep. Le système de développement GNUStep se compose des packages suivants : GNUstep MSYS System, GNUstep Core, GNUstep Devel, GNUstep Cairo, ProjectCenter IDE (comme Xcode, mais pas aussi complexe), Gorm (Interface Builder Like Xcode NIB builder). Ces programmes d'installation binaires n'ont pas été mis à jour depuis 2016, il pourrait donc être préférable de simplement installer en construisant sous Cygwin ou MSYS2 à la place.

Utilisation de la bibliothèque

Objective-C est aujourd'hui souvent utilisé en tandem avec une bibliothèque fixe d'objets standard (souvent appelés "kit" ou "framework"), tels que Cocoa , GNUstep ou ObjFW . Ces bibliothèques sont souvent livrées avec le système d'exploitation : les bibliothèques GNUstep sont souvent livrées avec des distributions basées sur Linux et Cocoa est livrée avec macOS. Le programmeur n'est pas obligé d'hériter des fonctionnalités de la classe de base existante (NSObject / OFObject). Objective-C permet la déclaration de nouvelles classes racines qui n'héritent d'aucune fonctionnalité existante. À l'origine, les environnements de programmation basés sur Objective-C offraient généralement une classe Object comme classe de base dont presque toutes les autres classes héritaient. Avec l'introduction d'OpenStep, NeXT a créé une nouvelle classe de base nommée NSObject, qui offrait des fonctionnalités supplémentaires par rapport à Object (un accent mis sur l'utilisation de références d'objets et le comptage de références au lieu de pointeurs bruts, par exemple). Presque toutes les classes de Cocoa héritent de NSObject.

Non seulement le changement de nom a servi à différencier le nouveau comportement par défaut des classes au sein de l'API OpenStep, mais il a permis au code qui utilisait Object - la classe de base d'origine utilisée sur NeXTSTEP (et, plus ou moins, d'autres bibliothèques de classes Objective-C) - de coexister dans le même runtime avec du code qui utilisait NSObject (avec quelques limitations). L'introduction du préfixe à deux lettres est également devenue une forme simpliste d'espaces de noms, ce qui manque à Objective-C. L'utilisation d'un préfixe pour créer un identifiant d'emballage informel est devenue une norme de codage informelle dans la communauté Objective-C, et continue à ce jour.

Plus récemment, des gestionnaires de packages ont fait leur apparition, comme CocoaPods , qui se veut à la fois gestionnaire de packages et référentiel de packages. Une grande partie du code Objective-C open source qui a été écrit au cours des dernières années peut désormais être installé à l'aide de CocoaPods.

Analyse de la langue

Les implémentations Objective-C utilisent un système d'exécution léger écrit en C, ce qui ajoute peu à la taille de l'application. En revanche, la plupart des systèmes orientés objet au moment de leur création utilisaient de grands runtimes de machines virtuelles . Les programmes écrits en Objective-C ont tendance à ne pas être beaucoup plus grands que la taille de leur code et celle des bibliothèques (qui n'ont généralement pas besoin d'être incluses dans la distribution du logiciel), contrairement aux systèmes Smalltalk où une grande quantité de mémoire était utilisé juste pour ouvrir une fenêtre. Les applications Objective-C ont tendance à être plus volumineuses que les applications C ou C++ similaires, car le typage dynamique Objective-C ne permet pas de supprimer ou d'intégrer les méthodes. Étant donné que le programmeur a une telle liberté de déléguer, de transférer des appels, de construire des sélecteurs à la volée et de les transmettre au système d'exécution, le compilateur Objective-C ne peut pas supposer qu'il est sûr de supprimer des méthodes inutilisées ou d'appeler des appels en ligne.

De même, le langage peut être implémenté sur des compilateurs C existants (dans GCC , d'abord en tant que préprocesseur, puis en tant que module) plutôt que comme un nouveau compilateur. Cela permet à Objective-C de tirer parti de l'énorme collection existante de code C, de bibliothèques, d'outils, etc. Les bibliothèques C existantes peuvent être encapsulées dans des wrappers Objective-C pour fournir une interface de style OO. Sous cet aspect, il est similaire à la bibliothèque GObject et au langage Vala , qui sont largement utilisés dans le développement d' applications GTK .

Tous ces changements pratiques ont abaissé la barrière à l'entrée , probablement le plus gros problème pour l'acceptation généralisée de Smalltalk dans les années 1980.

Une critique courante est qu'Objective-C ne prend pas en charge le langage pour les espaces de noms . Au lieu de cela, les programmeurs sont obligés d'ajouter des préfixes à leurs noms de classe, qui sont traditionnellement plus courts que les noms d'espace de noms et donc plus sujets aux collisions. Depuis 2007, toutes les classes et fonctions macOS de l' environnement de programmation Cocoa sont préfixées par "NS" (par exemple NSObject, NSButton) pour les identifier comme appartenant au noyau macOS ou iOS ; le "NS" dérive des noms des classes tels que définis lors du développement de NeXTSTEP .

Puisque Objective-C est un sur-ensemble strict de C, il ne traite pas les types primitifs C comme des objets de première classe .

Contrairement au C++ , Objective-C ne prend pas en charge la surcharge d'opérateurs . De plus, contrairement au C++, Objective-C permet à un objet d'hériter directement d'une seule classe (interdisant l' héritage multiple ). Cependant, dans la plupart des cas, les catégories et les protocoles peuvent être utilisés comme moyens alternatifs pour obtenir les mêmes résultats.

Étant donné qu'Objective-C utilise un typage d'exécution dynamique et que tous les appels de méthode sont des appels de fonction (ou, dans certains cas, des appels système), de nombreuses optimisations de performances courantes ne peuvent pas être appliquées aux méthodes Objective-C (par exemple : inlining, propagation constante, optimisations interprocédurales, et remplacement scalaire des agrégats). Cela limite les performances des abstractions Objective-C par rapport aux abstractions similaires dans des langages tels que C++ où de telles optimisations sont possibles.

Gestion de la mémoire

Les premières versions d'Objective-C ne supportaient pas le ramasse-miettes . À l'époque, cette décision faisait l'objet d'un débat, et de nombreuses personnes considéraient de longs "temps morts" (lorsque Smalltalk effectuait la collecte) pour rendre l'ensemble du système inutilisable. Certaines implémentations tierces ont ajouté cette fonctionnalité (notamment GNUstep utilisant Boehm ), et Apple l'a implémentée à partir de Mac OS X v10.5 . Cependant, dans les versions plus récentes de macOS et iOS, la récupération de place a été dépréciée au profit du comptage automatique de références (ARC), introduit en 2011.

Avec ARC, le compilateur insère automatiquement les appels de rétention et de libération dans le code Objective-C sur la base d' une analyse de code statique . L'automatisation évite au programmeur d'avoir à écrire du code de gestion mémoire. ARC ajoute également des références faibles au langage Objective-C.

Différences philosophiques entre Objective-C et C++

La conception et l'implémentation de C++ et Objective-C représentent des approches fondamentalement différentes pour étendre le C.

En plus du style de programmation procédurale de C, C++ prend directement en charge certaines formes de programmation orientée objet , de programmation générique et de métaprogrammation . C++ est également livré avec une grande bibliothèque standard qui comprend plusieurs classes de conteneurs . De même, Objective-C ajoute la programmation orientée objet , le typage dynamique et la réflexion à C. Objective-C ne fournit pas de bibliothèque standard en soi , mais dans la plupart des endroits où Objective-C est utilisé, il est utilisé avec un OpenStep -like bibliothèque comme OPENSTEP , Cocoa ou GNUstep , qui fournit des fonctionnalités similaires à la bibliothèque standard de C++.

Une différence notable est qu'Objective-C fournit un support d'exécution pour les fonctionnalités réfléchissantes , alors que C++ n'ajoute qu'une petite quantité de support d'exécution à C. En Objective-C, un objet peut être interrogé sur ses propres propriétés, par exemple, s'il répondra à un certain message. En C++, cela n'est pas possible sans l'utilisation de bibliothèques externes.

L'utilisation de la réflexion fait partie de la distinction plus large entre les fonctionnalités dynamiques (au moment de l'exécution) et les fonctionnalités statiques (au moment de la compilation) d'un langage. Bien que Objective-C et C++ utilisent chacun un mélange des deux fonctionnalités, Objective-C est résolument orienté vers les décisions d'exécution tandis que C++ est orienté vers les décisions de compilation. La tension entre la programmation dynamique et statique implique de nombreux compromis classiques dans la programmation : les fonctionnalités dynamiques ajoutent de la flexibilité, les fonctionnalités statiques ajoutent de la vitesse et la vérification de type.

La programmation générique et la métaprogrammation peuvent être implémentées dans les deux langages en utilisant le polymorphisme d'exécution . En C++, cela prend la forme de fonctions virtuelles et d'une identification de type à l'exécution , tandis qu'Objective-C offre un typage et une réflexion dynamiques. Objective-C et C++ prennent en charge le polymorphisme à la compilation ( fonctions génériques ), Objective-C n'ajoutant cette fonctionnalité qu'en 2015.

Voir également

Les références

Lectures complémentaires

  • Cox, Brad J. (1991). Programmation orientée objet : une approche évolutive . Addison Wesley. ISBN 0-201-54834-8.

Liens externes