-forwardInvocation работает с Clang - LLVM, но не с GCC - PullRequest
4 голосов
/ 21 сентября 2009

Следующий код реализует подкласс 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;
} 

Ответы [ 2 ]

6 голосов
/ 21 сентября 2009

Ваш прокси-класс не объявляет подпись сообщения для -floatValue. Так как он возвращает число с плавающей запятой, это может быть проблемой. Поскольку вы нигде не объявили это и, по-видимому, не приводите свой прокси-объект к его представленному классу, компилятор должен угадать сигнатуру метода. В этом случае компилятор GCC предполагает, что сообщение вернет указатель id.

В Objective-C, в зависимости от сигнатуры сообщения и архитектуры машины, для обработки сообщения и его возвращаемых значений используются разные функции. На машинах x86 сообщения, которые возвращают значение с плавающей запятой, вызываются через objc_msgSend_fpret, тогда как функции, которые возвращают void и id, используют objc_msgSend.

Поскольку компилятор GCC предполагает, что возвращаемое значение равно id, он использует последнюю функцию и неправильно обрабатывает результат. То, что Clang способен справиться с этим должным образом, интересно, но я бы не стал полагаться на такое поведение. Было бы лучше объявить категорию на вашем прокси для любых методов, которые вы будете перенаправлять. Это также имеет преимущество в удалении предупреждения, которое было сгенерировано для строки кода, вызывающей метод floatValue.

@interface Foo (ProxyMethods)
- (float)floatValue;
@end
0 голосов
/ 11 марта 2012

Любой метод init должен вызывать [super init]; для предотвращения непредвиденного поведения. Как так:

- (id)init {
    self = [super init];
    if (self) {
        // Your init code
    }
    return self;
}
...