4

typedef (void (^blockType)());Objective-C отливать тип блока в другой получил неожиданный результат

мне нужно отливать блоки с различными типами аргументов в тот же тип blockType и вызывать его как позже тип оригинала. Но есть проблема при выборе типа блокировки.

Следующий код работает хорошо с любым типом аргумента, ...

((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 

кроме поплавка:

((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 

, но это нормально, без литья:

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

как его объяснить и решить?

+3

Как его решить? Не делайте этого :) – jtbandes

+0

@jtbandes Вы правы :( – iwill

ответ

0

Объяснение: Вызов блока как (void (^)()), блок обрабатывается как (void (^)(double)).

Устранение: должно быть возвращено к (void (^)(float)) при вызове.

+2

Всегда можно вызвать функцию с помощью указателя функции (или указателя блока) другого типа. – jtbandes

+0

ABI отличается для разных номеров и типов параметров. Какие регистры или части стека используются для передачи значений и которые используются для возвращаемых значений и разных, и ABI отличается от разных архитектур. Я не верю, что есть безопасный способ гарантировать, что ваши настройки функций и преамбулы будут чтобы быть совместимым с тем, что вы пытаетесь сделать. Ответ на это не так: обычное решение состоит в том, чтобы каждая функция принимала один «id» (объект). Затем заверните все, что вам нужно передать в этот объект (NSNumber, NSDictionary, что угодно). –

4

Это, кажется, пятно старого доброго языка С. Рассмотрим следующий код (мы можем сказать, что это своего рода «перевод» вашего Obj-C блок с вопросами к C, насколько блоки связаны с указателями на функции (see here)):

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

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

Если вы звоните test вы увидите тот же результат, что и при вызове своего «больного» блока. Компилятор предоставит только предупреждение о несовместимых указателях и позволит вам запустить. Но если изменить

void (*pEmpty)();

в

void (*pEmpty)(void);

будет ошибка времени компиляции. То же самое произойдет, если вы добавите void явно к вашим блокам пустот, например. (void (^)(void) вместо (void (^)().

Причина такого поведения объяснены в C Standard:

Пустой список в функции описателя, которая не является частью определения этой функции указывает, что нет информации о количестве или типах задаются параметры .
§6.7.6.3-14 (с.134)

Таким образом, как это не означает, что не существует никаких параметров а скорее нет информации о них, литая проходит отлично.

Проблемы с неожиданным выходом является следующее:

Указатель на функцию одного типа может быть преобразован в указатель на функцию другого типа и обратно; результат сравнивается с исходным указателем. Если преобразованный указатель используется для вызова функции, тип которой несовместим с ссылочным типом, поведение не определено.
§6.3.2.3-8 (с.56)

и

Если функция определена с типом, который не совместим с типом (выражения ), на который указывает выражение, обозначающее вызываемую функцию, имеет значение undefined.
§6.5.2.2-9 (с.82)

Таким образом, кажется, что решение здесь так же, как @jtbandes сказал: не типы бардака блоков и редизайн этой части кода, чтобы избежать таких слепки.

 Смежные вопросы

  • Нет связанных вопросов^_^