Запуск performSelector: на объекте, который возвращает double, а не id

#objective-c #cocoa #performselector

#objective-c #cocoa #performselector

Вопрос:

Как я могу запустить произвольный селектор на объекте, возвращаемым значением которого является double? Например, у меня есть obj A, у которого есть метод -(double) бла;

Как я могу сделать double res = [obj performSelector:@selector(blah)]; ? performSelector возвращает объект id типа, поэтому должен ли я преобразовать id в NSInteger в double — это потеряет точность?

Кроме того, я не хочу использовать obj methodSignatureForSelector (что означает no NSMethodSignature и no NSInvocation ), потому что это приводит к огромной загрузке процессора во время выполнения.

Комментарии:

1. Почему NSInteger ? Почему бы и нет NSNumber ?

2. Компилятор Xcode предотвращает приведение к чему-либо, кроме NSInteger. Кроме того, NSNumber является объектом, но метод возвращает double, а не объект.

3. Но вы можете инициализировать NSNumber с помощью любого примитива. Включая double.

4. Хорошо, мой разум просто не хотел думать правильно, теперь я понимаю вашу проблему.

Ответ №1:

Возможно, вам захочется взглянуть на функции среды выполнения Objective-C, особенно objc_msgSend_fpret .

 double objc_msgSend_fpret( id self, SEL op, ... )
  

Который отправляет сообщение с возвращаемым значением с плавающей запятой экземпляру класса.

В performSelector методах используется objc_msgSend , который возвращает id тип.

Например:

 double res = objc_msgSend_fpret( obj, @selector( blah ) );
  

Вам нужно будет импортировать этот заголовок среды выполнения objc:

 #import <objc/message.h>
  

Редактировать

Кстати, вот ссылка на ссылку ObjC runtime: http://developer.apple.com/library/mac/#documentation/Cocoa/Reference/ObjCRuntimeRef/Reference/reference.html

ПРАВКА 2 — ВАЖНО

objc_msgSend_fpret реализуется по-разному, в зависимости от архитектуры процессора (в основном, i386 или x86_64).

Как я сказал в комментарии, эти функции реализованы с использованием assembly, поэтому их реализация зависит от архитектуры процессора.

В архитектуре x86_64 эта функция возвращает long double .

Вот почему он завершается с ошибкой (и возвращает NAN), когда вы присваиваете его a double .

Также обратите внимание, что существует функция objc_msgSend_fp2ret.

Итак, в принципе, мой предыдущий пример не будет работать:

 double x = objc_msgSend_fpret( obj, @selector( blah ) );
printf( "Val: %fn", x );
  

Как вы заметили, он выведет ‘NAN’.

Чтобы это сработало, вам придется сделать это следующим образом:

 long double x = objc_msgSend_fpret( obj, @selector( blah ) );
printf( "Val: %Lfn", x );
  

Вот рабочий пример:

http://www.eosgarden.com/uploads/misc/fp.m

Скомпилируйте его с помощью:

 gcc -Wall -framework Foundation -o fp fp.m
  

Комментарии:

1. objc_msgSend и друзья объявлены в objc/message.h . В Mac OS X вы можете просто импортировать objc/objc-runtime.h , но в iOS вы должны импортировать objc/message.h . Я отредактировал ответ.

2. @Macmade Странная вещь, но, хотя она должна работать, она возвращает NAN, где метод IMP возвращает допустимый результат. Не уверен, что я что-то упускаю.

3. @Macmade Чак выиграл бы, но мне не нравится, что он создает объект NSNumber, просто для приведения к doubleValue . Кроме того, MsgSend по-прежнему не работает … ` long double x = objc_msgSend_freet( [NSDate date], @selector( timeIntervalSinceReferenceDate ) );` x = NAN.

4. Нет точного способа. Все зависит от того, что вы хотите. Часто при решении таких проблем производительность может привести к другому подходу (что также может привести к проблемам). Это также отличается от того, действительно ли вы написали свой blah метод или он взят из библиотеки / фреймворка. Надеюсь, вы найдете правильный способ : )

Ответ №2:

Если это метод без аргументов, вы можете использовать valueForKey: и doubleValue для значения, возвращаемого этим методом. В противном случае, я думаю, вам придется поработать с objc_msgSend_fpret , чтобы заставить это работать.

Комментарии:

1. Значение, которое возвращается из этого метода, является double , поэтому использование ‘doubleValue’ для него или ‘valueForKey:’ не имеет смысла.

2. @David: valueForKey: примитивы boxes, так что да, то, что я сказал, действительно имеет смысл.

3. Я исправляюсь. Мне нравится ваше решение. Это было бы лучше всего, за исключением того, что это создает временный объект NSNumber.

Ответ №3:

Использует наименьший объем памяти (valueForKey: использует два временных объекта), и метод MsgSend не работает.

  IMP myImp1 = [obj methodForSelector:@selector(getDouble)];
 double aDouble1 = ((double (*) (id,SEL))myImp1)(obj,@selector(getDouble));
  

Комментарии:

1. Может быть, вы могли бы объяснить, почему вы считаете, что это лучшее решение?

2. Нет проблем : ) В этом конкретном случае я думаю, что использование objc_msgSend_fpret лучше и на самом деле более читабельно. Вы всегда должны использовать приведения функции с осторожностью.

3. Определенно более читаемый. С точки зрения безопасности, не уверен, что происходит внутри, но я не могу представить, что это делает гораздо больше, чем расширение до решения IMP.

4. На самом деле, все функции objc_msgSend * реализованы с использованием assembly, поэтому он может многое делать по-другому.

5. @Macmade Я попробовал ваш метод в пользу удобства чтения, но он не работает. Он возвращает только NAN, тогда как метод IMP возвращает допустимые результаты. Возьмем, к примеру, ` NSTimeInterval t = objc_msgSend_freet([NSDate date], @selector(timeIntervalSinceReferenceDate));` Возвращает NAN.