-forwardInvoction работает с Clang — LLVM, но не с GCC
-
12-09-2019 - |
Вопрос
Следующий код реализует подкласс NSProxy, который пересылает методы экземпляру NSNumber.
Однако при вызове [nsproxy floatValue] я получаю 0,0 в GCC 4.2.
Под LLVM-Clang я получаю правильный ответ 42.0.
Есть идеи, что происходит?
(кстати, это работает в разделе «Сборка мусора»)
-(id) init;
{
_result = [NSNumber numberWithFloat:42.0];
return self;
}
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
{
return [[_result class] instanceMethodSignatureForSelector:aSelector];
}
- (void)forwardInvocation:(NSInvocation *)anInvocation
{
[anInvocation setTarget:_result];
[anInvocation invoke];
return;
}
Решение
Ваш прокси-класс не объявляет подпись сообщения для -floatValue
.Поскольку он возвращает число с плавающей запятой, это может быть проблемой.Поскольку вы нигде его не объявили и, по-видимому, не приводите свой прокси-объект к представленному ему классу, компилятору приходится угадывать сигнатуру метода.В этом случае компилятор GCC предполагает, что сообщение вернет id
указатель.
В Objective-C, в зависимости от подписи сообщения и архитектуры машины, для обработки сообщения и его возвращаемых значений используются разные функции.На машинах x86 сообщения, возвращающие значение с плавающей запятой, вызываются через objc_msgSend_fpret
в то время как функции, которые возвращают void
и id
использовать objc_msgSend
.
Поскольку компилятор GCC предполагает, что возвращаемое значение является id
он использует последнюю функцию и неправильно обрабатывает результат.То, что Clang способен правильно с этим справиться, интересно, но я бы не стал полагаться на такое поведение.Было бы лучше объявить категорию на вашем прокси для всех методов, которые вы будете пересылать.Это также дает преимущество в удалении предупреждения, которое создавалось для строки кода, вызывающей floatValue
метод.
@interface Foo (ProxyMethods)
- (float)floatValue;
@end
Другие советы
Любой init
метод должен вызывать [super init];
для предотвращения неожиданного поведения.Итак:
- (id)init {
self = [super init];
if (self) {
// Your init code
}
return self;
}