Заметка
Доступ к этой странице требует авторизации. Вы можете попробовать войти в систему или изменить каталог.
Доступ к этой странице требует авторизации. Вы можете попробовать сменить директорию.
Язык Objective-C основан на селекторах. Селектор — это сообщение, которое можно отправить в объект или класс. Xamarin.iOS сопоставляет селекторы экземпляров с методами экземпляра и селекторами классов со статическими методами.
В отличие от обычных функций C (и таких как функции-члены C++), вы не можете напрямую вызвать селектор с помощью P/Invoke Вместо этого, селекторы отправляются в Objective-C класс или экземпляр с помощью функции objc_msgSend Функции.
Дополнительные сведения о сообщениях Objective-Cсм. в руководстве apple по работе с объектами .
Пример
Предположим, вы хотите вызвать sizeWithFont:forWidth:lineBreakMode: селектор в NSString.
Объявление (из документации Apple) — это:
- (CGSize)sizeWithFont:(UIFont *)font forWidth:(CGFloat)width lineBreakMode:(UILineBreakMode)lineBreakMode
Этот API имеет следующие характеристики:
- Тип возвращаемого значения предназначен
CGSizeдля единого API. - Параметр
font— это UIFont (и тип (косвенно), производный от NSObject, и сопоставляется с System.IntPtr. - Параметр
width, aCGFloat, сопоставляется сnfloat. - Параметр
lineBreakMode, aUILineBreakMode, уже был привязан в Xamarin.iOS в качествеUILineBreakModeПеречисления.
Сложив все вместе, objc_msgSend объявление должно соответствовать:
CGSize objc_msgSend(
IntPtr target,
IntPtr selector,
IntPtr font,
nfloat width,
UILineBreakMode mode
);
Объявите его следующим образом:
[DllImport (Constants.ObjectiveCLibrary, EntryPoint="objc_msgSend")]
static extern CGSize cgsize_objc_msgSend_IntPtr_float_int (
IntPtr target,
IntPtr selector,
IntPtr font,
nfloat width,
UILineBreakMode mode
);
Чтобы вызвать этот метод, используйте следующий код:
NSString target = ...
Selector selector = new Selector ("sizeWithFont:forWidth:lineBreakMode:");
UIFont font = ...
nfloat width = ...
UILineBreakMode mode = ...
CGSize size = cgsize_objc_msgSend_IntPtr_float_int(
target.Handle,
selector.Handle,
font == null ? IntPtr.Zero : font.Handle,
width,
mode
);
Если возвращаемое значение было структурой, размер которой меньше 8 байт (например, более старый SizeF , используемый перед переходом на объединенные API), приведенный выше код будет выполняться на симуляторе, но произошел сбой на устройстве. Чтобы вызвать селектор, возвращающий значение меньше 8 бит в размере, объявите функцию objc_msgSend_stret :
[DllImport (MonoTouch.Constants.ObjectiveCLibrary, EntryPoint="objc_msgSend_stret")]
static extern void cgsize_objc_msgSend_stret_IntPtr_float_int (
out CGSize retval,
IntPtr target,
IntPtr selector,
IntPtr font,
nfloat width,
UILineBreakMode mode
);
Чтобы вызвать этот метод, используйте следующий код:
NSString target = ...
Selector selector = new Selector ("sizeWithFont:forWidth:lineBreakMode:");
UIFont font = ...
nfloat width = ...
UILineBreakMode mode = ...
CGSize size;
if (Runtime.Arch == Arch.SIMULATOR)
size = cgsize_objc_msgSend_IntPtr_float_int(
target.Handle,
selector.Handle,
font == null ? IntPtr.Zero : font.Handle,
width,
mode
);
else
cgsize_objc_msgSend_stret_IntPtr_float_int(
out size,
target.Handle, selector.Handle,
font == null ? IntPtr.Zero: font.Handle,
width,
mode
);
Вызов селектора
Вызов селектора состоит из трех шагов:
- Получите целевой объект селектора.
- Получите имя селектора.
- Вызов
objc_msgSendс соответствующими аргументами.
Целевые объекты селектора
Целевой объект селектора — это экземпляр объекта или Objective-C класс. Если целевой объект является экземпляром и получен из привязанного типа Xamarin.iOS, используйте ObjCRuntime.INativeObject.Handle это свойство.
Если целевой объект является классом, используйте ObjCRuntime.Class для получения ссылки на экземпляр класса, а затем используйте Class.Handle это свойство.
Имена селекторов
Имена селекторов перечислены в документации Apple. Например, NSString включает sizeWithFont: и sizeWithFont:forWidth:lineBreakMode: селекторы. Внедренные и конечные двоеточия являются частью имени селектора и не могут быть опущены.
Получив имя селектора, можно создать ObjCRuntime.Selector для него экземпляр.
Вызов objc_msgSend
objc_msgSend отправляет сообщение (селектор) в объект. Это семейство функций принимает по крайней мере два обязательных аргумента: целевой объект селектора (дескриптор экземпляра или класса), сам селектор и все аргументы, необходимые для селектора. Аргументы экземпляра и селектора должны быть System.IntPtr, и все остальные аргументы должны соответствовать типу, который ожидает селектор, например nint для intселектора или System.IntPtr для всех NSObjectпроизводных типов. Используйте NSObject.Handle свойство для получения IntPtr экземпляра Objective-C типа.
Существует несколько objc_msgSend функций:
- Используется
objc_msgSend_stretдля селекторов, возвращающих структуру. В ARM сюда входят все типы возвращаемых данных, которые не являются перечислением или любым из встроенных типов C (char, ,short,longint,float).doubleВ x86 (симуляторе) этот метод необходимо использовать для всех структур размером более 8 байт (CGSize8 байтов и не используетсяobjc_msgSend_stretв симуляторе). - Используйте
objc_msgSend_fpretдля селекторов, возвращающих значение с плавающей запятой только в x86. Эта функция не требуется использовать в ARM; вместо этого используйтеobjc_msgSend. - Основная функция objc_msgSend используется для всех остальных селекторов.
Когда вы решите, какие objc_msgSend функции необходимо вызвать (симулятор и устройство могут требовать другой метод), можно использовать обычный [DllImport] метод для объявления функции для последующего вызова.
Набор готовых objc_msgSend объявлений можно найти в ObjCRuntime.Messaging.
Различные вызовы на симуляторе и устройстве
Как описано выше, Objective-C имеет три типа objc_msgSend методов: один для регулярных вызовов, один для вызовов, возвращающих значения с плавающей запятой (только x86), и один для вызовов, возвращающих значения структуры. Последний включает суффикс _stret в ObjCRuntime.Messaging.
При вызове метода, возвращающего определенные структуры (правила, описанные ниже), необходимо вызвать метод с возвращаемым значением в качестве первого параметра в качестве out значения:
// The following returns a PointF structure:
PointF ret;
Messaging.PointF_objc_msgSend_stret_PointF_IntPtr (out ret, this.Handle, selConvertPointFromWindow.Handle, point, window.Handle);
Правило, когда использовать _stret_ метод отличается от x86 и ARM.
Если вы хотите, чтобы привязки работали как на симуляторе, так и на устройстве, добавьте код, например следующий:
if (Runtime.Arch == Arch.DEVICE)
{
PointF ret;
Messaging.PointF_objc_msgSend_stret_PointF_IntPtr (out ret, myHandle, selector.Handle);
return ret;
}
else
{
return Messaging.PointF_objc_msgSend_PointF_IntPtr (myHandle, selector.Handle);
}
Использование метода objc_msgSend_stret
При построении для ARM используйте objc_msgSend_stretдля любого типа значения, который не является перечислением или любым из базовых типов для перечисления (int, , byte, shortlong, ). floatdouble
При сборке для x86 используйте objc_msgSend_stretдля любого типа значения, который не является перечислением или любым из базовых типов для перечисления (int, , byte, shortlong, double) floatи собственный размер которого превышает 8 байт.
Создание собственных подписей
При необходимости можно использовать следующий gist для создания собственных подписей.