在performSelector的结果中,使用float / double / CGFloat强制转换是一个非常奇怪的行为:
为什么这样做?
BOOL property = (BOOL)[self.object performSelector:@selector(boolProperty)];
NSInteger property = (NSInteger) [self.object performSelector:@selector(integerProperty)];
这不是
CGFloat property = (CGFloat) [self.object performSelector:@selector(floatProperty)];
起初我试图这样做:
CGFloat property = [[self.object performSelector:@selector(floatProperty)] floatValue];
但我最终得到了EXC_BAD_ACCESS运行时错误。我已经找到了解决这个问题的黑客,但我想了解为什么它适用于Integer和Bool,而不是浮点类型。
我的黑客:
@implementation NSObject (AddOn)
-(CGFloat)performFloatSelector:(SEL)aSelector
{
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[[self class] instanceMethodSignatureForSelector:aSelector]];
[invocation setSelector:aSelector];
[invocation setTarget:self];
[invocation invoke];
CGFloat f = 0.0f;
[invocation getReturnValue:&f];
return f;
}
@end
然后:
CGFloat property = [self.object performFloatSelector:@selector(floatProperty)];
答案 0 :(得分:4)
performSelector
方法仅支持不返回任何对象的方法。这在其文档中有所说明:
对于返回除对象以外的任何内容的方法,请使用
NSInvocation
。
你的" hack"只是调用返回非对象的选择器的正确方法。
它似乎适用于整数和布尔方法的原因是方法返回值的方式。 performSelector
的返回类型是id
,是对象指针类型。类似整数的值;其中包括NSInteger
,BOOL
和对象指针;通常在通用寄存器中返回,但浮点值通常在浮点寄存器中返回。编译器将始终从用于返回id
值的寄存器中加载结果,然后执行强制转换所需的任何操作。对于整数和布尔值,加载的值可能是正确的,并且在这些情况下强制转换为无操作,因此所有(看起来)都有效。对于浮点值,加载的值不正确 - 它不是被调用方法返回的值,因为它位于不同的寄存器中。
注意使用performSelector
调用非对象,非空的选择器可能会导致ARC下的内存问题 - 编译器通常会警告是否可能出现这种情况。
调用选择器的NSInvocation
方式适用于非对象返回类型,因为它的一个参数是方法签名本身。使用签名NSInvocation
能够确定返回值的类型及其返回方式,因此可以正确地将其返回给调用者。
HTH
答案 1 :(得分:1)
如果您不想使用NSInvocation
,可以直接使用objc_msgSend
函数(这是编译器将消息调用转换为的内容):
BOOL (*f)(id, SEL) = (BOOL (*)(id, SEL))objc_msgSend;
BOOL property = f(self.object, @selector(boolProperty));
NSInteger (*f)(id, SEL) = (NSInteger (*)(id, SEL))objc_msgSend;
NSInteger property = f(self.object, @selector(integerProperty));
CGFloat (*f)(id, SEL) = (CGFloat (*)(id, SEL))objc_msgSend;
CGFloat property = f(self.object, @selector(floatProperty));
请注意,您必须将其强制转换为具有方法的确切签名的函数指针才能正确调用它;这里的方法不带参数,因此该函数只有2个“隐藏”参数,self
和_cmd
。如果方法采用参数,则必须向函数指针类型添加更多参数。
另请注意,对于struct
返回,您需要使用objc_msgSend_stret
代替objc_msgSend
,但其他所有内容都与上述内容完全相同。