- 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
, aCGFloat
, mappas tillnfloat
. - parametern
lineBreakMode
, aUILineBreakMode
, har redan bundits i Xamarin.iOS somUILineBreakMode
upprä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:
- få väljarmålet.
- hämta väljarens namn.
- 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 inteobjc_msgSend_stret
i simulatorn). - använd
objc_msgSend_fpret
för väljare som returnerar ett flyttalsvärde endast på x86. Denna funktion behöver inte användas på ARM; använd iställetobjc_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_msgSend
metoder: 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.