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.
Selon Aaron Hillegass, une personne exérimentée en C peut apprendre Objective C en deux heures 2
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.
}
Version fin 2009 a fin 2011 : Snow Lepoard (10.6)
A partir de cette version, une nouvelle version de MacOS sort chaque année, au lieu de tous les deux ans jusque ici.
GCD et NSBlockOperation
Ajout des blocs et du GCD (grand central dispatch),
s’appuye sur les NSOperation
et les NSOperationQueue qui ont remplacé les threads POSIX,
leur ajoute NSBlockOperation.
ARC
Premiere version de l’ARC, plus besoin de gerer -(void)dealloc,
plus besoin d’appeler [alloc] : [[NSString alloc] initWithFormat:@"..."] devient [NSString stringWithFormat: @"..."].
Ce systeme ajoute des instructions release, autorelease, retain
automatiquement pour nous (on doit toujours appeller alloc)
en fonction des conventions de nommage de nos fonctions, il
génère la fonction -(void)dealloc à notre place.
Il se base sur les conventions de nommage des fonction :
toute fonction commencant par alloc, new, copy,
mutableCopy ou init est supposée allouer de la memoire, ARC
ajoute pour nous autorelease pour tous les abjets
renvoyés par ces fonctions.
Appeller les fonctions manuellement devient non necessaire, du code bricolé comme ceci n’est plus autorisé
while([x retainCount])
[x release];
Il faut maintenant réflechir en termes de pointeurs strong
et weak pour gérer les reeférences cycliques. Les nouveaux
pointeurs __weak se mettent automatiquement à nil quand
l’objet auxquels ils réfères est déaloué. Il faut
remplacer @property (assign) par @property (weak)
Un pointeur __strong qui réfère à un objet est considéré
comme ajoutant un retain dans le compte de références :
quand un tel pointeur sort de sa portée, release est rajouté
par ARC, ce qui rend dealloc inutile.
__unsafe_unretained simule les pointeurs classiques
qu’il faut gérer soi meme.
Voila comment (copy) est réduit
/// objc 1.X
-(NSString*)title {
return title;
}
-(void)setTitle:(NSString*)newTitle {
if(title!=newTitle) {
[title release];
title = [newTitle copy];
}
}
/// objc 2.X
@property (copy) NSString* title; // dans le .h
@synthetize title = _title; // dans le .m, inutile avec autosynthetize
nonatomic est utile pour les méthodes qui ne le sont pas.
Slides disponibles dans la WWDC 2011 : https://archive.org/details/wwdc2011-slides/322_objective_c_advancements_in_depth/page/n27/mode/1up 🌍⤴ Video de la WWDC 2012 identique aux slides : WWDC 2012: Adopting Automatic Reference Counting 🌍⤴
Version fin 2011 a fin 2012 : Lion (10.7)
Améliorations de l’ARC. Migration depuis GCC vers LLVM/Clang, de gdb vers lldb, preliminaire a swift.
Apport de @autoreleasepool qui remplace ‘[[NSAutoreleasePool alloc] init]’ et [pool drain]. Vu que NSAutoreleasePool n’est pas
un vrai objet que l’on peut retain, ARC ne peut pas travailler
avec, une nouvelle construction de langage devient nécessaire.
Version 2012 : Mountain Lion (10.8) XCode 4.4
- Déprécation du garbage collector
- Notations raccourcies pour les primitives de Foundation.
NSNumber *n = @42; // au lieu de [NSNumber numberWithInteger:42]
NSNumber *ari = @(40 + 2); // arithmetique
NSArray *a = @[@42, @"hello", @12];
NSDictionnary *d = @{foo: @"bar", toto: @:"tata"};
NSString*s = @("toto"); // "toto" peut egalement etre une fonction retournant char*
a[0];
d[foo]; // syntaxe avec crochets pour tableaux et dictionnaires
- Des rustines pour aider à la migration vers l’ARC quand les methodes n’ont pas une denomination standart.
- Synthese automatique, plus besoin de
@synthetise: On peut ecriretoto.tata = @42au lieu de[toto setTata:[NSNumber numberWithInteger:42]];pour peu que le “getter” et “setter” soient correctement nommés.
Plus de détails : What’s new in Cocoa, session 204, WWDC 2012, 35:00 🌍⤴
ARC : autosynthese
ARC remplace la gestion manuelle via retain/release/autorelease.
Les variables d’instance dont les clients de l’interface n’ont pas besoin de connaitre peuvent etre déclarés dans l’implementation. L’autosynthese reduit encore le code necessaire pour les propriétés d’un objet.
methode et propriétés privées implicites
Le compilateur de 2012 peut trouver les attributs
et methodes d’interface déclarés dans le fichier d’implementation,
plus besoin de déclarer des catégories ou des extensions de classe
au sein de fichier .m pour les méthodes. Les attributs privés
peuvent etre déclares dans les crochet du @implementation
@interface Thing {
NSString* private; /// detail visible aux usagers
}
@end
/// devient
@interface Thing { /// fichier .h, allégé
}
@end
@implementation Thing { /// .m, invisible aux tiers
NSString* private; /// trouvable par le compilateur
}
@end
Les méthodes “privées” peuvent maintenant simplement etre présentes
dans l’implémentation, plus besoin d’écrire des catégories
d’extension de son interface dans le .m, voila du code qui
devient inutile
@interface MonObjet (Prive) /// categorie, typage faible
-(void)methodePrive;
-(void)autreMethodePrive;
@end
@interface MonObjet () /// extension de classe, erreur compilateur si methodes manquent
-(void)methodePrive;
-(void)autreMethodePrive;
@end
Plus d’info sur la doc : https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/ObjectiveC/Chapters/ocCategories.html 🌍⤴ Video explicative : Migrating to modern Objective C : 18:00 🌍⤴
Cours en ligne
- https://web.stanford.edu/class/cs193e/Downloads/ 🌍⤴ CS 193 - Stanford
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 🌍⤴
- https://www.noodlesoft.com/blog/2009/09/25/sticky-section-headers-in-nstableview/ 🌍⤴ Noodlesoft
- https://blog.andymatuschak.org/post/173646741/your-new-friends-obj-c-associated-objects 🌍⤴ Difficile a naviguer, en parlait dans le temps
- https://www.massicotte.org/blog/ 🌍⤴ Plutot sur swift
- http://theocacao.com 🌍⤴ Bonne facture
Interface utilisateur
- https://developer.apple.com/design/human-interface-guidelines 🌍⤴
- Plus ancienne et toujours valide, condensé : https://leopard-adc.pepas.com/documentation/UserExperience/Conceptual/AppleHIGuidelines/XHIGIntro/XHIGIntro.html 🌍⤴
- https://www.windowmaker.org 🌍⤴ Interface destinée au moteur de rendu X11 basé sur GNUStep
Emulateur NeXTStep
Archives WWDC (avant WWDC 2014)
- https://archive.org/search?query=subject%3A%22WWDC%22 🌍⤴
- https://wwdcnotes.com/documentation/wwdcnotes 🌍⤴ Notes resumant les videos (open source)
References
-
https://hopl4.sigplan.org/details/hopl-4-papers/18/The-Origins-of-Objective-C-at-PPI-Stepstone-and-its-Evolution-at-NeXT 🌍⤴ ↩︎
-
Cocoa Programming for Mac Os X, Aaron Hillegass, Adison Weseley Professional, 2002, p.4 ↩︎