4

typedef (void (^blockType)());Objective-C jeta un type de bloc à un autre résultat inattendu a

Je dois jeter des blocs avec différents types d'arguments dans un même type blockType, et l'invoquons comme le type d'origine plus tard. Mais il y a un problème lors de la conversion du type de bloc.

Le code suivant fonctionne bien avec tout type d'argument, ...

((blockType)^(BOOL b) { 
    NSLog(@"BOOL: %d", b); 
})(YES); // >> BOOL: 1 
((blockType)^(int i) { 
    NSLog(@"int: %d", i); 
})(1); // >> int: 1 
((blockType)^(double f) { 
    NSLog(@"double: %f", f); 
})(1.0/3); // >> double: 0.333333 
((blockType)^(NSString *s) { 
    NSLog(@"NSString *: %@", @"string"); 
})(1.0/3); // >> NSString *: string 

sauf flotteur:

((blockType)^(float f) { 
    NSLog(@"float: %f", f); 
})(1.0f); // >> float: 0.000000 
((blockType)^(float f) { 
    NSLog(@"float: %f", f); 
})(1.0f/3); // >> float: 36893488147419103232.000000 

mais il est ok sans coulée:

(^(float f) { 
    NSLog(@"float without casting: %f", f); 
})(1.0/3); // >> float without casting: 0.333333 

comment l'expliquer et le résoudre? Explication: Appelant le bloc (void (^)()), le bloc est traité comme (void (^)(double)).

+3

Comment le résoudre? Ne faites pas cela :) – jtbandes

+0

@jtbandes Vous avez raison :( – iwill

Répondre

0

Résoudre: Doit être renvoyé à (void (^)(float)) lors de l'appel.

+2

Il est toujours dangereux d'appeler une fonction via un pointeur de fonction (ou un pointeur de bloc) d'un type différent – jtbandes

+0

L'ABI est différent pour différents nombres et types de paramètres. Quels registres ou parties de la pile sont utilisés pour transmettre des valeurs et qui sont utilisés pour renvoyer des valeurs et différents, et l'ABI est différent sur différentes architectures Je ne crois pas qu'il existe un moyen sûr de faire en sorte que vos configurations de fonctions et préambules Pour être compatible avec ce que vous essayez de faire, la réponse est de ne pas faire ceci: la solution habituelle est de faire accepter à chaque fonction un seul 'id' (objet) .Pour ensuite envelopper tout ce dont vous avez besoin pour passer dans cet objet (NSNumber, NSDictionary, peu importe). –

4

Il semble être une souillure du bon vieux langage C. Considérez le code suivant (on peut dire qu'il est une sorte de « traduction » de votre bloc Obj-C avec des questions à C dans la mesure où les blocs sont liés à des pointeurs de fonction (see here)):

void test() 
{ 
    void (*pEmpty)(); 
    pEmpty = functionFloat; 
    pEmpty(1.0f/3); 
} 

void functionFloat(float f) 
{ 
    printf("float: %f", f); 
} 

Si vous appelez test vous verrez le même résultat que lorsque vous invoquez votre bloc «malade». Le compilateur fournira juste un avertissement sur les pointeurs incompatibles et vous laissera courir. Mais si vous changez

void (*pEmpty)();

à

void (*pEmpty)(void);

il y aura une erreur de compilation. La même chose se produira si vous ajoutez void explicitement à vos blocs vides, par ex. (void (^)(void) au lieu de (void (^)().

La raison d'un tel comportement est expliqué dans le C Standard:

La liste vide dans un déclarateur de fonction qui ne fait pas partie d'une définition de cette fonction spécifie que aucune information sur le nombre ou le type de les paramètres sont fournis.
§6.7.6.3-14 (p.134)

Ainsi, comme cela ne signifie pas qu'il n'y a pas de paramètres mais plutôt pas d'info sur eux, casting passe bien.

Le problème avec la sortie inattendue est la suivante:

Un pointeur vers une fonction d'un type peut être converti en un pointeur sur une fonction d'un autre type et vice-versa; le résultat doit être égal au pointeur d'origine. Si un pointeur converti est utilisé pour appeler une fonction dont le type n'est pas compatible avec le type référencé, le comportement n'est pas défini.
§6.3.2.3-8 (p.56)

et

Si la fonction est définie avec un type qui n'est pas compatible avec le type (de l'expression ) pointée par la expression qui dénote la fonction appelée, le comportement est indéfini.
§6.5.2.2-9 (p.82)

Ainsi, il semble que la solution est comme @jtbandes dit ici: types de blocs ne pas mess et reconception cette partie du code pour éviter une telle moules.