Objc
Crée en 1981 par Brad Cox pour profiter la syntaxe “productive” de Smalltalk tout en restant compatible avec les outils UNIX et l’environnement C existant.1
Il s’execute sur une machine virtuelle qui permet de modifier dynamiquement la configuration des objets, en leur ajoutant, modifiant ou enlevant des méthodes, et de modifier la hierarchie entre eux pendant l’execution du programme, sans necesiter de recompilation, de la même manière que Smalltalk.
Implementation
GNUstep met son implemtentation en libre acces via github, ce qui peut etre utile pour comprendre les mecaniques internes du langage et de son runtime en particulier.
On peut y voir par exemple que les methodes enregistrées dans une
classe sont accessibles par une liste chainée, ou comment une
classe est crée “en vif” dans runtime.c.
Premier programme en Objective C
// fichier oc.c
#include <stdio.h>
int main()
{
printf("bonjour objective C");
}
On le compile et execute avec la commande cc -ObjC oc.c && ./a.out, et il affiche bien bonjour objective C. Un programme C
est en effet un programme objective C valide.
Si le fichier a l’extension .m au lieu de .c, le compilateur
fera automatiquement la compilation en mode -ObjC et il
n’est pas nécessaire de rajouter l’option au compilateur.
Exemple plus avancé
Voici un exemple de programme qu’on peut réaliser en Objective-C mais pas en C ou C++.
Si vous êtes dans un environnement GnuStep, objc.h inclut
tout l’environnement et toutes les fonctions du langage.
Sur Apple, on y trouve uniquement quelques types
de base du langage comme id, SEL, BOOL,
et unde dizaine de fonctions rajoutées avec Mac OS X
parmi lesquelles
sel_getName, sel_registerName, object_getClassName…
il faudra donc rajouter soi meme <objc/runtime.h> pour
faire quoi que ce soit d’intéressant sur Apple.
D’autre part, un objet qui ne déclare pas de parent doit au
minimum déclarer les méthodes methodSignatureForSelector:
et doesNotRecognizeSelector:. Pour gagner du temps
nous allons utiliser la classe NSObject fournie pour
nous par le framework Foundation. Pour que le programme
compile il faudra ajouter l’option -framework Foundation
à la ligne de commande.
#include <objc/objc.h>
#include <objc/runtime.h>
#include <stdio.h>
#include <Foundation/Foundation.h>
// @ est utilisé par le préprocesseur du langage
// pour générer au runtime la hiérarchie et la
// constitution des classes. On declare que nos
// objets heritent des fonctionnalités de NSObject,
// notamment sa methode `alloc`, `respondsToSelector`, etc.
@interface chien: NSObject
-(void)aboye;
@end
@implementation chien
-(void)aboye {
printf("ouaf (chien)");
}
@end
@interface loup: NSObject
-(void)aboye;
@end
@implementation loup
-(void)aboye {
printf("ouaf (loup)");
}
@end
@interface chat: NSObject
@end
@implementation chat
@end
void miaule(void)
{
printf("miaou");
}
int main()
{
id t = [chien alloc]; // id est un pointeur générique
printf("%s \n", object_getClassName(t)); // "chien"
id chi = class_createInstance([chien class], 0); // autre facon d'initialiser un chien
chi = [chi init]; // alloc aloue juste la memoire, init y met des valeurs qui sieent a un objet fraichement crée (par ex tout a zero)
[t aboye]; // "ouaf (chien)"
[chi aboye]. // "ouaf (chien)"
SEL s = sel_registerName("aboye"); // ajoute un selecteur dans la
// base de données puis retourne un pointeur dessus,
// on associera plus tard le selecteur a une fonction
printf("%s \n", sel_getName(s)); // "aboye"
loup * lo = [loup alloc]; // on ne se soucie pas de "init" ici
chat * ch = [chat alloc]; // idem
NSArray* arr = @[chi, lo, ch]; // NSArray est générique
for(int i = 0; i < [arr count]; i++) {
printf("%s ", object_getClassName(arr[i]));
if([arr[i] respondsToSelector:s]) {
[arr[i] performSelector:s];
}
puts("");
}
// la boucle ci dessus affiche
// chien ouaf (chien)
// loup ouaf (loup)
// chat
// le chat ne possède pas de methode "aboye" donc
// ne fait rien.
class_addMethod([chat class], s, miaule, "vv");
// on lui on ajoute une methode qui "repond" au selecteur "aboye".
// Noter que le nom du selecteur n'a pas de rapport avec le nom
// de la fonction. Il s'agit juste d'une chaine de caracteres
// convertie en un pointeur de type SEL par souci de performance.
puts("take2");
for(int i = 0; i < [arr count]; i++) {
printf("%s ", object_getClassName(arr[i]));
if([arr[i] respondsToSelector:s]) {
[arr[i] performSelector:s];
}
puts("");
}
// affiche
// chien: ouaf (chien)
// loup: ouaf (loup)
// chat: miaou
NSLog(@"%@\n%lu\n%lu\n", [arr description], [arr retainCount], [ch retainCount]);
// affiche
// 2026-02-12 06:39:58.618 a.out[34915:48582009] (
// "<chien: 0x12de10d30>",
// "<loup: 0x12de10b50>",
// "<chat: 0x12de10da0>"
// )
// 1
// 2
//
// `NSLog` peut afficher les objets de type NSString, NSNumber, etc.
// [NSOject description] affiche une vue utile au debugage
// [NSOject retainCount] affiche le nombre de propriétaires d'un objet
// on voit donc que NSArray prend possession des objets qu'on
// lui fournit
[arr release]; // detruit le NSArray
NSLog(@"retain %lu\n", [ch retainCount]);
// "1", supprimer le tableau ne supprime pas les objets
// qu'il contient, a moins d'avoir appellé `release` sur
// ces dits objets une fois placés dans le tableau.
}
Blogs en parlant
- https://lapcatsoftware.com/blog/2008/10/20/working-without-a-nib-part-7-the-empire-strikes-back/ 🌍⤴
- http://blog.hyperjeff.net/code?id=292 🌍⤴
Interface utilisateur
- https://developer.apple.com/design/human-interface-guidelines 🌍⤴
- Plus ancienne et toujours valide, condensé : https://leopard-adc.pepas.com/documentation/AppleApplications/Conceptual/AutomatorConcepts/Articles/DevelopAction.html#//apple_ref/doc/uid/TP40001511-BAJBICGH 🌍⤴