#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.