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

Interface utilisateur

References