Objective-C的铸块类型到另一个得到意想不到的结果

问题描述:

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

说明:将该块称为(void (^)()),该块被视为(void (^)(double))

解决方法:调用时必须回退到(void (^)(float))

+2

通过不同类型的函数指针(或块指针)调用函数总是很危险的 – jtbandes

+0

对于不同数量和类型的参数,ABI是不同的。哪些寄存器或堆栈的一部分用于传递值,哪些用于返回值和不同的值,而且ABI在不同的架构上有所不同。我不相信有任何安全的方法可以确保函数设置和前导码正在运行与你想要做的事情兼容,答案是不要这样做,通常的解决方案是让每个函数都接受一个'id'(对象),然后把你需要传递的东西包装到那个对象中NSNumber,NSDictionary,不管)。 –

它似乎是一个很好的旧C语言的污点。考虑下面的代码(我们可以说这是一种与问题到C您的OBJ-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页)

如果函数与一类是不带有(所述 表达的)的类型兼容所定义的指向表示被调用函数的表达式,其行为是未定义的 。
§6.5.2.2-9(第82页)

所以,似乎这里的解决方案就像@jtbandes说:不乱块类型和重新设计这部分代码,以避免这种管型。