2010-05-12 12 views
6

Lorsque j'ai des définitions conflictuelles des ivars d'une classe dans objective-c (ne pas redéclarer la classe dans le même fichier, mais plutôt nommer la même classe avec diff ivars, aucun avertissement ou mieux encore n'est émis par le compilateur. , les deux ensembles de Ivars sont utilisables par les méthodes appropriées dans les fichiers respectifs par exempleCompilateur GCC - comportement bogue ou non spécifié?

Foo.m.

@interface foo { 
int a; 
} 
- (int)method; 
@end 

@implementation foo 

- (int)method { 
    return a; 
} 

@end 

bar.m:

@interface foo { 
float baz; 
} 

@end 

@implementation foo (category) 
- (float)blah { 
    return baz; 
} 
@end 

compile sans avertissements ou erreurs. Est cela intentionnel? Est-ce une erreur non vérifiée? (Pour l'enregistrement, un et baz sont en fait le même emplacement mémoire.)

Edit: pour le disque que je parle de l'iPhone OS, que je crois utilise le même temps d'exécution de 64 bits MacOS

Répondre

18

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).

+0

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 –

+0

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

0

Je pense ce que vous avez fait est extended la classe.

Questions connexes