Objective-C Xamarin.iOS의 선택기

언어는 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에 대한 것입니다.
  • 매개 변수 fontuifontnsobject에서 파생 된 형식 (간접적) 이며, 이는 System.IntPtr에 매핑됩니다.
  • width 매개 변수인 aCGFloat는 .에 매핑됩니다nfloat.
  • lineBreakMode 매개 변수는 이미 Xamarin.iOS에 UILineBreakMode/&I로 바인딩되었습니다.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바이트 미만의 구조체(예: 통합 API로 전환하기 전에 사용된 이전 SizeF 버전)였다면 위의 코드는 시뮬레이터에서 실행되었지만 디바이스에서 충돌했습니다. 크기가 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
    );

선택기 호출

선택기 호출에는 다음 세 가지 단계가 있습니다.

  1. 선택기 대상을 가져옵니다.
  2. 선택기 이름을 가져옵니다.
  3. 적절한 인수를 사용하여 호출 objc_msgSend 합니다.

선택기 대상

선택기 대상은 개체 인스턴스 또는 클래스입니다 Objective-C . 대상이 인스턴스이고 바인딩된 Xamarin.iOS 형식에서 온 경우 속성을 사용합니다 ObjCRuntime.INativeObject.Handle .

대상이 클래스인 경우 클래스 인스턴스에 대한 참조를 가져오는 데 사용한 ObjCRuntime.Class 다음 속성을 사용합니다 Class.Handle .

선택기 이름

선택기 이름은 Apple 설명서에 나열됩니다. 예를 들어 포함 NSStringsizeWithFont:sizeWithFont:forWidth:lineBreakMode: 선택기입니다. 포함된 콜론과 후행 콜론은 선택기 이름의 일부이며 생략할 수 없습니다.

선택기 이름이 있으면 인스턴스를 ObjCRuntime.Selector 만들 수 있습니다.

통화 objc_msgSend

objc_msgSend 는 메시지(선택기)를 개체에 보냅니다. 이 함수 패밀리는 선택기 대상(인스턴스 또는 클래스 핸들), 선택기 자체 및 선택기에서 필요한 인수 등 두 개 이상의 필수 인수를 사용합니다. 인스턴스 및 선택기 인수는 System.IntPtr모두 다시 기본 인수는 선택기에서 예상하는 형식(예nint: 모든 파생 형식에 대한 int인수)System.IntPtrNSObject과 일치해야 합니다. 사용NSObject.Handle형식 인스턴스에 대한 Objective-C 속성을 IntPtr 가져옵니다.

objc_msgSend 개 이상의 함수가 있습니다.

  • 구조체를 반환하는 선택기에 사용합니다 objc_msgSend_stret . ARM에서는 열거형이 아닌 모든 반환 형식 또는 C 기본 제공 형식(char,, short, int, longfloat, double)이 포함됩니다. x86(시뮬레이터)에서 이 메서드는 크기가 8바이트보다 큰 모든 구조체(CGSize 8바이트이며 시뮬레이터에서는 사용되지 않음)에 사용해야 objc_msgSend_stret 합니다.
  • x86에서만 부동 소수점 값을 반환하는 선택기에 사용합니다 objc_msgSend_fpret . 이 함수는 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, short, long, doublefloat)의 경우

x86용으로 빌드할 때 objc_msgSend_stret열거형이 아니거나 열거형의 기본 형식(int, , byte, short, long, doublefloat)이 아니고 네이티브 크기가 8바이트보다 큰 값 형식의 경우

고유한 서명 만들기

필요한 경우 다음 요지를 사용하여 고유한 서명을 만들 수 있습니다.