Bien évidemment rompu, ce code devrait compiler sans avertissement dans tous les cas simplement parce que le compilateur ne dispose pas de suffisamment d'informations pour savoir comment mettre en garde. Correctement compilé, il génère une erreur de linker complètement différente seulement en 64 bits (ce qui est une retombée du nouvel ABI Objective-C, pas directement des ivars non-fragiles).
Si vous ajoutez à int main() {}
foo.m puis compiler avec la ligne de commande gcc -arch x86_64 foo.m -lobjc
, les erreurs de lien disparaissent parce que la bibliothèque d'exécution objc fournit les symboles vtable vides nécessaires pour compléter le lien. Pensez à chaque fichier .m comme une unité de compilation isolée pendant la compilation. Lorsque le compilateur compile un fichier .m, il ne connaît que ce qui se trouve dans ce fichier .m, ce qui est fourni par tout ce qui est importé dans ce fichier .m et - si un projet est configuré pour cela - ce qui est défini dans l'en-tête précompilé du projet.
Ainsi, quand vous dites que dans bar.m:
@interface foo {
float baz;
}
@end
@implementation foo (category)
- (float)blah {
return baz;
}
@end
int main() {}
Le compilateur n'a aucune notion de la déclaration en foo.m. Le code généré décrit une catégorie sur la classe foo qui accède à l'ivar baz. Si cette classe n'existe pas à l'heure du lien, une erreur sera jeté maintenant, compte tenu de votre foo.m et bar.m avec mon ajout d'une fonction principale comme ci-dessus, nous allons essayer quelques compilations différentes:
gcc -arch i386 foo.m -lobjc
Undefined symbols:
"_main", referenced from:
start in crt1.10.6.o
ld: symbol(s) not found
collect2: ld returned 1 exit status
Cela a du sens car nous n'avons pas défini de fonction main() dans foo.m. La compilation 64 bits fait la même chose.
gcc -arch i386 bar.m -lobjc
Compile et des liens sans avertissement. Pour comprendre pourquoi, regardez les symboles générés (supprimés une douzaine de non pertinents):
nm -a a.out
00001f52 t -[foo(category) blah]
00000000 A .objc_category_name_foo_category
Ainsi, le binaire contient une catégorie nommée category
la classe foo
. Aucune erreur de lien car l'éditeur de liens n'essaie pas réellement de résoudre les catégories. Il suppose que la classe foo
apparaîtra comme par magie avant que la catégorie ne soit résolue au moment de l'exécution.
Vous pouvez suivre avec la résolution classe/catégorie de l'exécution avec un Ivar:
env OBJC_PRINT_CLASS_SETUP=YES ./a.out
objc[498]: CONNECT: pending category 'foo (category)'
objc[498]: CONNECT: class 'Object' now connected (root class)
objc[498]: CONNECT: class 'Protocol' now connected
objc[498]: CONNECT: class 'List' now connected
Ainsi, la catégorie a été marquée comme étant en attente. Le moteur d'exécution se connectera dès que foo
verra le jour!
Maintenant, 64 bits ...
gcc -arch x86_64 bar.m -lobjc
Undefined symbols:
"_OBJC_IVAR_$_foo.baz", referenced from:
-[foo(category) blah] in ccvX4uIk.o
"_OBJC_CLASS_$_foo", referenced from:
l_OBJC_$_CATEGORY_foo_$_category in ccvX4uIk.o
objc-class-ref-to-foo in ccvX4uIk.o
ld: symbol(s) not found
Les erreurs de liaison sont parce que le moderne Objective-C ABI provoque effectivement des symboles propres à émettre pour les variables d'instance et les catégories pour diverses raisons, y compris l'ajout de métadonnées Cela peut aider à valider les programmes (comme dans le cas présent).
Aucune erreur de compilation (comportement correct) et les erreurs de liaison n'ont de sens. Maintenant, que diriez-vous de relier les deux ensemble?
Dans le cas de 32 bits, tout compile et relie sans erreur. Ainsi, nous devons examiner les symboles et au débogage ObjC crachent pour voir ce qui se passe:
gcc -arch i386 bar.m foo.m -lobjc
nm -a a.out
00001e0f t -[foo method]
00001dea t -[foo(category) blah]
00000000 A .objc_category_name_foo_category
00003070 S .objc_class_name_foo
env OBJC_PRINT_CLASS_SETUP=YES ./a.out
objc[530]: CONNECT: attaching category 'foo (category)'
objc[530]: CONNECT: class 'Object' now connected (root class)
objc[530]: CONNECT: class 'Protocol' now connected
objc[530]: CONNECT: class 'List' now connected
objc[530]: CONNECT: class 'foo' now connected (root class)
Aha! Maintenant, il y a une classe foo
et le runtime connecte la catégorie à la classe au démarrage. Évidemment, la méthode retournant le baz
va échouer spectaculairement.
L'éditeur de liens 64 bits échoue, cependant:
gcc -arch x86_64 bar.m foo.m -lobjc
Undefined symbols:
"_OBJC_IVAR_$_foo.baz", referenced from:
-[foo(category) blah] in ccBHNqzm.o
ld: symbol(s) not found
collect2: ld returned 1 exit status
Avec l'ajout des symboles pour les variables d'instance, l'éditeur de liens peut maintenant saisir les situations où une classe a été redéclarée mal (comme cela a été fait dans le @interface
de levure de bière).
selon ma question initiale: est-ce un comportement intentionnel ou une erreur non vérifiée de la part du compilateur? De plus, étant pour l'iPad, il est testé dans l'environnement "moderne" mais sur le simulateur, ce qui signifie qu'il est réellement i386, donc il suivrait les règles 64bit ou 32bit comme défini ci-dessus (comme vous avez utilisé ces termes plutôt que nouveaux/ancien runtime), et plus important encore, cela changerait-il sur l'appareil? Et enfin, en ce qui concerne le retour du baz ivar, aucun échec spectaculaire ne se produit car c'est le même emplacement de mémoire que le, comme en témoigne * (int *) & baz donnant la valeur d'un –
Le devrait et compile sans avertissement. Cela provoquera une erreur de liaison sur la compilation du bureau 64 bits et lors du ciblage de l'appareil, mais sera bien lié (mais exécuté de manière erronée) dans le simulateur. Si vous vous attendez à pouvoir stocker une valeur à la fois dans 'baz' et' a', elle échouera de façon spectaculaire car, dans le runtime du simulateur, les deux partagent efficacement l'espace de stockage. – bbum