Objective – C-väljare i Xamarin.iOS

  • 07/12/2017
  • 4 minuter att läsa
    • d
    • d
    • c
    • a

Objective – C-språket är baserat på väljare. En väljare är amessage som kan skickas till ett objekt eller en klass. Xamarin.iOS kartor instans selectorsto instans metoder, och klass väljare till statiska metoder.

till skillnad från normala C-funktioner (och som C++-medlemsfunktioner) kan du inte direkt åberopa en väljare medp/Invoke istället skickas väljare till en Objective-C-klass eller instans med funktionenobjc_msgSend.

för mer information om meddelanden i Objective-C, ta en titt på Apple ’ sworking with Objectsguide.

exempel

anta att du vill anropasizeWithFont:forWidth:lineBreakMode: – väljaren på NSString.Deklarationen (från Apples dokumentation) är:

- (CGSize)sizeWithFont:(UIFont *)font forWidth:(CGFloat)width lineBreakMode:(UILineBreakMode)lineBreakMode

detta API har följande egenskaper:

  • returtypen är CGSize för Unified API.
  • parametern font är en uifont (och en typ (indirekt) härledd från NSObject och mappas till System.IntPtr.
  • parametern width, a CGFloat, mappas till nfloat.
  • parametern lineBreakMode, a UILineBreakMode, har redan bundits i Xamarin.iOS som UILineBreakModeuppräkning.

att sätta ihop allt, objc_msgSend deklarationen ska matcha:

CGSize objc_msgSend( IntPtr target, IntPtr selector, IntPtr font, nfloat width, UILineBreakMode mode);

förklara det enligt följande:

static extern CGSize cgsize_objc_msgSend_IntPtr_float_int ( IntPtr target, IntPtr selector, IntPtr font, nfloat width, UILineBreakMode mode);

för att ringa den här metoden, använd kod som följande:

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);

hade det returnerade värdet varit en struktur som var mindre än 8 byte i storlek (som den äldre SizeF som användes innan du bytte till Unified API: er), skulle ovanstående kod ha kört på simulatorn men kraschat på enheten. Om du vill anropa en väljare som returnerar ett värde som är mindre än 8 bitar i storlek anger du funktionen 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);

för att ringa den här metoden, använd kod som följande:

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 );

anropa en väljare

anropa en väljare har tre steg:

  1. få väljarmålet.
  2. hämta väljarens namn.
  3. Ring objc_msgSend med lämpliga argument.

Väljarmål

ett väljarmål är antingen en objektinstans eller en Objective-C-klass. Ifmålet är en instans och kom från en bunden Xamarin.iOS-Typ, använd egenskapen ObjCRuntime.INativeObject.Handle.

om målet är en klass, använd ObjCRuntime.Class för att få en referens till classinstance, använd sedan egenskapen Class.Handle.

Väljarnamn

Väljarnamn listas i Apples dokumentation. Till exempel innehåller NSString sizeWithFont: och sizeWithFont:forWidth:lineBreakMode: väljare. De inbäddade och bakre kolonnen är en del av väljarnamnet och kan inte utelämnas.

när du har ett väljarnamn kan du skapa en ObjCRuntime.Selector – instans för den.

anropar objc_msgSend

objc_msgSend skickar ett meddelande (väljare) till ett objekt. Denna familj avfunktioner tar minst två nödvändiga argument: väljarmålet (aninstance eller klasshandtag), väljaren själv och eventuella argumenter som krävs för väljaren. Argumenten för instans och väljare måste varaSystem.IntPtr, och alla återstående argument måste matcha typen theselector förväntar sig, till exempel en nint för en int eller enSystem.IntPtr för alla NSObject-härledda typer. Använd egenskapenNSObject.Handle för att få en IntPtr för en instans av Objective-C-typ.

det finns mer än en objc_msgSend funktion:

  • använd objc_msgSend_stret för väljare som returnerar en struktur. På ARM, detta inkluderar alla returtyper som inte är en uppräkning eller någon av de inbyggda C-typerna (char,short, int, long, float, double). På x86 (simulatorn) måste denna metod användas för alla strukturer som är större än 8 byte i storlek (CGSize är 8 byte och använder inte objc_msgSend_stret i simulatorn).
  • använd objc_msgSend_fpretför väljare som returnerar ett flyttalsvärde endast på x86. Denna funktion behöver inte användas på ARM; använd istället objc_msgSend.
  • mainobjc_msgSendfunction används för alla andra väljare.

när du har bestämt vilken objc_msgSend funktion(er) du behöver ringa(simulator och enhet kan var och en kräva en annan metod) kan du använda en normal metod för att deklarera funktionen för senare anrop.

en uppsättning färdiga objc_msgSend-deklarationer finns iObjCRuntime.Messaging.

olika anrop på simulator och enhet

som beskrivits ovan har Objective-C tre typer av objc_msgSendmetoder: en för regelbundna anrop, en för anrop som returnerar flytande punktvärden (endast x86) och en för anrop som returnerar strukturvärden. Det senare inkluderar suffixet _stret iObjCRuntime.Messaging.

om du åberopar en metod som returnerar vissa strukturer (regler som beskrivs nedan) måste du åberopa metoden med returvärdet som firstparameter som ett out – värde:

// The following returns a PointF structure:PointF ret;Messaging.PointF_objc_msgSend_stret_PointF_IntPtr (out ret, this.Handle, selConvertPointFromWindow.Handle, point, window.Handle);

regeln för när man ska använda metoden _stret_ skiljer sig åt på x86 och ARM.Om du vill att dina bindningar ska fungera på både simulatorn och enheten,Lägg till kod som följande:

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);}

använda objc_msgsend_stretmetoden

när du bygger för ARM, användobjc_msgSend_stret för alla värdetyper som inte är en uppräkning eller någon av bastypernaför en uppräkning (int, byte, short, long, double, float).

när du bygger för x86, användobjc_msgSend_stret för alla värdetyper som inte är en uppräkning eller någon av bastypernaför en uppräkning (int, byte, short, long, double, float)och vars ursprungliga storlek är större än 8 byte.

skapa egna signaturer

följande kontentan kan användas för att skapa egna signaturer, om det behövs.

Lämna ett svar

Din e-postadress kommer inte publiceras.