Objective c 执行选择器转换

Objective c 执行选择器转换,objective-c,reflection,casting,performselector,Objective C,Reflection,Casting,Performselector,对performSelector的结果进行float/double/CGFloat转换时,出现了一种非常奇怪的行为: 为什么这样做有效 BOOL property = (BOOL)[self.object performSelector:@selector(boolProperty)]; NSInteger property = (NSInteger) [self.object performSelector:@selector(integerProperty)]; 但事实并非如此 CGFlo

对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
然后:


performSelector
方法仅支持不返回任何内容或对象的方法。其文件中包括了这一点,其中说明:

对于返回对象以外的任何内容的方法,请使用
NSInvocation

您的“hack”只是调用返回非对象的选择器的正确方法

它似乎适用于整型和布尔型方法的原因与方法返回值的方式有关。
performSelector
的返回类型是
id
,一种对象指针类型。类整数值;其中包括
NSInteger
BOOL
和对象指针;通常在通用寄存器中返回,但浮点值通常在浮点寄存器中返回。编译器将始终从用于返回
id
值的寄存器加载结果,然后执行强制转换所需的任何操作。对于整型值和布尔值,加载的值可能是正确的,在这些情况下,强制转换是不可操作的,因此一切(看起来)都正常。对于浮点值,加载的值不正确-它不是被调用方法返回的值,因为它位于不同的寄存器中

注意:使用
performSelector
调用非对象、非void、选择器可能会导致ARC下的内存问题-如果出现这种情况,编译器通常会发出警告

调用选择器的
NSInvocation
方法适用于非对象返回类型,因为它的参数之一是方法签名本身。使用签名
NSInvocation
可以确定返回值的类型和返回方式,从而正确地将其返回给调用者


HTH

如果不想使用
NSInvocation
,可以直接使用
objc\u msgSend
函数(这是编译器将消息调用转换为的):

请注意,您必须将其强制转换为具有方法确切签名的函数指针,才能正确调用它;这里的方法不带参数,因此函数只有两个“隐藏”参数,
self
\u cmd
。如果方法采用参数,则必须向函数指针类型添加更多参数


另外,请注意,对于
struct
返回,您将需要使用
objc\u msgSend\u stretc
代替
objc\u msgSend
,但其他所有内容与上述内容完全相同。

第一个“工作?”性能选择器应该返回一个
id
。。。也许将
id
转换为long比将其转换为float更安全。看。我仍然想知道为什么这个协议与类是分开的,但不管怎样,它们是有效的,我有单元测试来证明这一点。被接受的答案解释了它为什么有效。@JulianJ.Tejera-答案完全证明了围绕“工作!”的空话是正确的,在ARC中不允许使用
id
to
BOOL
NSInteger
。这是MRC吗?我使用的是ARC,Xcode没有抱怨。我想我必须多读一点关于寄存器的内容。我用#pragma-clang-diagnostic-push#pragma-clang-diagnostic-ignored“-Warc-performSelector-leaks”抑制了编译器警告。学习新的操作方法确实很有趣,但我认为我会坚持NSInvocation解决方案,因为语法类似于objective-c,而且更干净(至少对我来说)。谢谢
CGFloat property = [self.object performFloatSelector:@selector(floatProperty)];
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));