Notatka
Dostęp do tej strony wymaga autoryzacji. Może spróbować zalogować się lub zmienić katalogi.
Dostęp do tej strony wymaga autoryzacji. Możesz spróbować zmienić katalogi.
W tym dokumencie opisano różne optymalizacje stosowane w czasie kompilacji dla aplikacji platform Xamarin.iOS i Xamarin.Mac.
Usuń element UIApplication.EnsureUIThread/ NSApplication.EnsureUIThread
Usuwa wywołania elementu UIApplication.EnsureUIThread (dla platformy Xamarin.iOS) lub NSApplication.EnsureUIThread (dla platformy Xamarin.Mac).
Ta optymalizacja spowoduje zmianę następującego typu kodu:
public virtual void AddChildViewController (UIViewController childController)
{
global::UIKit.UIApplication.EnsureUIThread ();
// ...
}
w następujący sposób:
public virtual void AddChildViewController (UIViewController childController)
{
// ...
}
Ta optymalizacja wymaga włączenia konsolidatora i jest stosowana tylko do metod z atrybutem [BindingImpl (BindingImplOptions.Optimizable)] .
Domyślnie jest ona włączona dla kompilacji wydania.
Domyślne zachowanie można przesłonić, przekazując --optimize=[+|-]remove-uithread-checks do mtouch/mmp.
Inline IntPtr.Size
W linii stałej IntPtr.Size wartości zgodnie z platformą docelową.
Ta optymalizacja spowoduje zmianę następującego typu kodu:
if (IntPtr.Size == 8) {
Console.WriteLine ("64-bit platform");
} else {
Console.WriteLine ("32-bit platform");
}
w następujący sposób (podczas kompilowania dla 64-bitowej platformy):
if (8 == 8) {
Console.WriteLine ("64-bit platform");
} else {
Console.WriteLine ("32-bit platform");
}
Ta optymalizacja wymaga włączenia konsolidatora i jest stosowana tylko do metod z atrybutem [BindingImpl (BindingImplOptions.Optimizable)] .
Domyślnie jest ona włączona, jeśli jest przeznaczona dla pojedynczej architektury lub zestawu platformy (Xamarin.iOS.dll, Xamarin.TVOS.dll, Xamarin.WatchOS.dll lub Xamarin.Mac.dll).
Jeśli jest przeznaczona dla wielu architektur, ta optymalizacja spowoduje utworzenie różnych zestawów dla wersji 32-bitowej i 64-bitowej aplikacji, a obie wersje będą musiały zostać uwzględnione w aplikacji, co skutecznie zwiększy ostateczny rozmiar aplikacji zamiast go zmniejszyć.
Domyślne zachowanie można przesłonić, przekazując --optimize=[+|-]inline-intptr-size do mtouch/mmp.
Wbudowany obiekt NSObject.IsDirectBinding
NSObject.IsDirectBinding jest właściwością wystąpienia, która określa, czy określone wystąpienie jest typu otoki, czy nie (typ otoki jest typem zarządzanym, który mapuje na typ natywny; na przykład typ zarządzany UIKit.UIView mapuje na typ natywny UIView — przeciwieństwo jest typem użytkownika, w tym przypadku class MyUIView : UIKit.UIView jest typem użytkownika).
Należy znać wartość IsDirectBinding wywołania Objective-Cmetody , ponieważ wartość określa, która wersja objc_msgSend ma być używana.
Biorąc pod uwagę tylko następujący kod:
class UIView : NSObject {
public virtual string SomeProperty {
get {
if (IsDirectBinding) {
return "true";
} else {
return "false"
}
}
}
}
class NSUrl : NSObject {
public virtual string SomeOtherProperty {
get {
if (IsDirectBinding) {
return "true";
} else {
return "false"
}
}
}
}
class MyUIView : UIView {
}
Możemy określić, że w UIView.SomeProperty wartości IsDirectBinding elementu nie jest stałą i nie można jej podkreślić:
void uiView = new UIView ();
Console.WriteLine (uiView.SomeProperty); /* prints 'true' */
void myView = new MyUIView ();
Console.WriteLine (myView.SomeProperty); // prints 'false'
Jednak istnieje możliwość przyjrzenia się wszystkim typom w aplikacji i ustaleniu, że nie ma żadnych typów dziedziczyjących z NSUrlklasy , a zatem można bezpiecznie w tekście wartości do IsDirectBinding stałej true:
void myURL = new NSUrl ();
Console.WriteLine (myURL.SomeOtherProperty); // prints 'true'
// There's no way to make SomeOtherProperty print anything but 'true', since there are no NSUrl subclasses.
W szczególności ta optymalizacja spowoduje zmianę następującego typu kodu (jest to kod powiązania dla NSUrl.AbsoluteUrlelementu ):
if (IsDirectBinding) {
return Runtime.GetNSObject<NSUrl> (global::ObjCRuntime.Messaging.IntPtr_objc_msgSend (this.Handle, Selector.GetHandle ("absoluteURL")));
} else {
return Runtime.GetNSObject<NSUrl> (global::ObjCRuntime.Messaging.IntPtr_objc_msgSendSuper (this.SuperHandle, Selector.GetHandle ("absoluteURL")));
}
w następującej sekcji (gdy można określić, że w aplikacji nie ma podklas NSUrl ):
if (true) {
return Runtime.GetNSObject<NSUrl> (global::ObjCRuntime.Messaging.IntPtr_objc_msgSend (this.Handle, Selector.GetHandle ("absoluteURL")));
} else {
return Runtime.GetNSObject<NSUrl> (global::ObjCRuntime.Messaging.IntPtr_objc_msgSendSuper (this.SuperHandle, Selector.GetHandle ("absoluteURL")));
}
Ta optymalizacja wymaga włączenia konsolidatora i jest stosowana tylko do metod z atrybutem [BindingImpl (BindingImplOptions.Optimizable)] .
Zawsze jest ona domyślnie włączona dla platformy Xamarin.iOS i zawsze wyłączona dla platformy Xamarin.Mac (ponieważ istnieje możliwość dynamicznego ładowania zestawów na platformie Xamarin.Mac, nie można ustalić, czy określona klasa nigdy nie jest podklasowana).
Domyślne zachowanie można przesłonić, przekazując --optimize=[+|-]inline-isdirectbinding do mtouch/mmp.
Wbudowane środowisko uruchomieniowe.Arch
Ta optymalizacja spowoduje zmianę następującego typu kodu:
if (Runtime.Arch == Arch.DEVICE) {
Console.WriteLine ("Running on device");
} else {
Console.WriteLine ("Running in the simulator");
}
w następujący sposób (podczas kompilowania dla urządzenia):
if (Arch.DEVICE == Arch.DEVICE) {
Console.WriteLine ("Running on device");
} else {
Console.WriteLine ("Running in the simulator");
}
Ta optymalizacja wymaga włączenia konsolidatora i jest stosowana tylko do metod z atrybutem [BindingImpl (BindingImplOptions.Optimizable)] .
Jest ona zawsze włączona domyślnie dla platformy Xamarin.iOS (nie jest dostępna dla platformy Xamarin.Mac).
Domyślne zachowanie można przesłonić, przekazując --optimize=[+|-]inline-runtime-arch do mtouch.
Eliminacja martwego kodu
Ta optymalizacja spowoduje zmianę następującego typu kodu:
if (true) {
Console.WriteLine ("Doing this");
} else {
Console.WriteLine ("Not doing this");
}
Do:
Console.WriteLine ("Doing this");
Spowoduje to również ocenę stałych porównań, w następujący sposób:
if (8 == 8) {
Console.WriteLine ("Doing this");
} else {
Console.WriteLine ("Not doing this");
}
i określ, czy wyrażenie 8 == 8 jest zawsze prawdziwe, i zmniejsz je do:
Console.WriteLine ("Doing this");
Jest to zaawansowana optymalizacja w przypadku użycia wraz z optymalizacjami inliningu, ponieważ może przekształcić następujący typ kodu (jest to kod powiązania dla NFCIso15693ReadMultipleBlocksConfiguration.Rangeelementu ):
NSRange ret;
if (IsDirectBinding) {
if (Runtime.Arch == Arch.DEVICE) {
if (IntPtr.Size == 8) {
ret = global::ObjCRuntime.Messaging.NSRange_objc_msgSend (this.Handle, Selector.GetHandle ("range"));
} else {
global::ObjCRuntime.Messaging.NSRange_objc_msgSend_stret (out ret, this.Handle, Selector.GetHandle ("range"));
}
} else if (IntPtr.Size == 8) {
ret = global::ObjCRuntime.Messaging.NSRange_objc_msgSend (this.Handle, Selector.GetHandle ("range"));
} else {
ret = global::ObjCRuntime.Messaging.NSRange_objc_msgSend (this.Handle, Selector.GetHandle ("range"));
}
} else {
if (Runtime.Arch == Arch.DEVICE) {
if (IntPtr.Size == 8) {
ret = global::ObjCRuntime.Messaging.NSRange_objc_msgSendSuper (this.SuperHandle, Selector.GetHandle ("range"));
} else {
global::ObjCRuntime.Messaging.NSRange_objc_msgSendSuper_stret (out ret, this.SuperHandle, Selector.GetHandle ("range"));
}
} else if (IntPtr.Size == 8) {
ret = global::ObjCRuntime.Messaging.NSRange_objc_msgSendSuper (this.SuperHandle, Selector.GetHandle ("range"));
} else {
ret = global::ObjCRuntime.Messaging.NSRange_objc_msgSendSuper (this.SuperHandle, Selector.GetHandle ("range"));
}
}
return ret;
w tym celu (podczas kompilowania dla urządzenia 64-bitowego, a także gdy można upewnić się, że w aplikacji nie NFCIso15693ReadMultipleBlocksConfiguration ma podklas):
NSRange ret;
ret = global::ObjCRuntime.Messaging.NSRange_objc_msgSend (this.Handle, Selector.GetHandle ("range"));
return ret;
Kompilator AOT jest już w stanie wyeliminować w ten sposób martwy kod, ale ta optymalizacja jest wykonywana wewnątrz konsolidatora, co oznacza, że konsolidator może zobaczyć, że istnieje wiele metod, które nie są już używane, i w związku z tym mogą zostać usunięte (chyba że są używane gdzie indziej):
global::ObjCRuntime.Messaging.NSRange_objc_msgSend_stretglobal::ObjCRuntime.Messaging.NSRange_objc_msgSendSuperglobal::ObjCRuntime.Messaging.NSRange_objc_msgSendSuper_stret
Ta optymalizacja wymaga włączenia konsolidatora i jest stosowana tylko do metod z atrybutem [BindingImpl (BindingImplOptions.Optimizable)] .
Jest ona zawsze włączona domyślnie (gdy konsolidator jest włączony).
Domyślne zachowanie można przesłonić, przekazując --optimize=[+|-]dead-code-elimination do mtouch/mmp.
Optymalizowanie wywołań do elementu BlockLiteral.SetupBlock
Środowisko uruchomieniowe Xamarin.iOS/Mac musi znać sygnaturę bloku podczas tworzenia Objective-C bloku dla zarządzanego delegata. Może to być dość kosztowna operacja. Ta optymalizacja obliczy sygnaturę bloku w czasie kompilacji i zmodyfikuje il, aby wywołać metodę SetupBlock , która zamiast tego przyjmuje podpis jako argument. Dzięki temu nie trzeba obliczać podpisu w czasie wykonywania.
Testy porównawcze pokazują, że przyspiesza to wywoływanie bloku przez współczynnik od 10 do 15.
Spowoduje to przekształcenie następującego kodu:
public static void RequestGuidedAccessSession (bool enable, Action<bool> completionHandler)
{
// ...
block_handler.SetupBlock (callback, completionHandler);
// ...
}
Do:
public static void RequestGuidedAccessSession (bool enable, Action<bool> completionHandler)
{
// ...
block_handler.SetupBlockImpl (callback, completionHandler, true, "v@?B");
// ...
}
Ta optymalizacja wymaga włączenia konsolidatora i jest stosowana tylko do metod z atrybutem [BindingImpl (BindingImplOptions.Optimizable)] .
Jest ona domyślnie włączona w przypadku używania statycznego registrar (w środowisku Xamarin.iOS statyczny jest registrar domyślnie włączony w przypadku kompilacji urządzeń, a na platformie Xamarin.Mac statyczny registrar jest domyślnie włączony dla kompilacji wydań).
Domyślne zachowanie można przesłonić, przekazując --optimize=[+|-]blockliteral-setupblock do mtouch/mmp.
Optymalizowanie obsługi protokołów
Środowisko uruchomieniowe Xamarin.iOS/Mac wymaga informacji na temat sposobu implementowania Objective-C protokołów przez typy zarządzane. Te informacje są przechowywane w interfejsach (i atrybutach w tych interfejsach), które nie są bardzo wydajnym formatem, ani nie są przyjazne dla konsolidatora.
Jednym z przykładów jest to, że te interfejsy przechowują informacje o wszystkich elementach członkowskich protokołu w atrybucie [ProtocolMember] , który zawiera między innymi odwołania do typów parametrów tych elementów członkowskich. Oznacza to, że po prostu zaimplementowanie takiego interfejsu sprawi, że konsolidator zachowa wszystkie typy używane w tym interfejsie, nawet w przypadku opcjonalnych elementów członkowskich aplikacja nigdy nie wywołuje ani nie implementuje.
Ta optymalizacja sprawi, że magazyn statyczny registrar będzie zawierać wszystkie wymagane informacje w wydajnym formacie, który używa niewielkiej ilości pamięci, która jest łatwa i szybka do znalezienia w czasie wykonywania.
Nauczy również konsolidatora, że nie musi zachowywać tych interfejsów ani żadnych powiązanych atrybutów.
Ta optymalizacja wymaga włączenia konsolidatora i statycznego registrar .
W systemie Xamarin.iOS ta optymalizacja jest domyślnie włączona, gdy zarówno konsolidator, jak i statyczny registrar są włączone.
Na platformie Xamarin.Mac ta optymalizacja nigdy nie jest domyślnie włączona, ponieważ platforma Xamarin.Mac obsługuje dynamiczne ładowanie zestawów, a te zestawy mogą nie być znane w czasie kompilacji (a tym samym nie są zoptymalizowane).
Domyślne zachowanie można przesłonić, przekazując --optimize=-register-protocols do mtouch/mmp.
Usuwanie dynamicznego registrar
Środowisko uruchomieniowe Xamarin.iOS i Xamarin.Mac obejmują obsługę rejestrowania typów zarządzanych w środowisku uruchomieniowym Objective-C . Można to zrobić w czasie kompilacji lub w czasie wykonywania (lub częściowo w czasie kompilacji i w spoczynku w czasie wykonywania), ale jeśli jest całkowicie wykonywane w czasie kompilacji, oznacza to, że kod pomocniczy do wykonywania można usunąć. Powoduje to znaczne zmniejszenie rozmiaru aplikacji, w szczególności w przypadku mniejszych aplikacji, takich jak rozszerzenia lub aplikacje systemu watchOS.
Ta optymalizacja wymaga włączenia zarówno statycznego registrar , jak i konsolidatora.
Konsolidator podejmie próbę ustalenia, czy można bezpiecznie usunąć dynamiczną metodę registrar, a jeśli tak, spróbuje go usunąć.
Ponieważ platforma Xamarin.Mac obsługuje dynamiczne ładowanie zestawów w czasie wykonywania (które nie były znane w czasie kompilacji), nie można określić w czasie kompilacji, czy jest to bezpieczna optymalizacja. Oznacza to, że ta optymalizacja nigdy nie jest domyślnie włączona dla aplikacji platformy Xamarin.Mac.
Domyślne zachowanie można przesłonić, przekazując --optimize=[+|-]remove-dynamic-registrar do mtouch/mmp.
Jeśli wartość domyślna zostanie zastąpiona w celu usunięcia dynamicznego registrar, konsolidator będzie emitować ostrzeżenia, jeśli wykryje, że nie jest bezpieczny (ale dynamika registrar nadal zostanie usunięta).
Inline Runtime.DynamicRegistrationSupported
Wylinij wartość Runtime.DynamicRegistrationSupported określaną w czasie kompilacji.
Jeśli dynamiczny registrar zostanie usunięty (zobacz Usuwanie optymalizacji dynamicznej registrar ), jest to stała false wartość, w przeciwnym razie jest to stała true wartość.
Ta optymalizacja spowoduje zmianę następującego typu kodu:
if (Runtime.DynamicRegistrationSupported) {
Console.WriteLine ("do something");
} else {
throw new Exception ("dynamic registration is not supported");
}
w następujących przypadkach po usunięciu dynamicznego registrar :
throw new Exception ("dynamic registration is not supported");
w następujących przypadkach, gdy dynamika registrar nie zostanie usunięta:
Console.WriteLine ("do something");
Ta optymalizacja wymaga włączenia konsolidatora i jest stosowana tylko do metod z atrybutem [BindingImpl (BindingImplOptions.Optimizable)] .
Jest ona zawsze włączona domyślnie (gdy konsolidator jest włączony).
Domyślne zachowanie można przesłonić, przekazując --optimize=[+|-]inline-dynamic-registration-supported do mtouch/mmp.
Wstępnie skompiluj metody tworzenia zarządzanych delegatów dla Objective-C bloków
Gdy Objective-C wywołuje selektor, który przyjmuje blok jako parametr, a następnie kod zarządzany zastąpi tę metodę, środowisko uruchomieniowe Xamarin.iOS / Xamarin.Mac musi utworzyć delegata dla tego bloku.
Kod powiązania wygenerowany przez generator powiązań będzie zawierać [BlockProxy] atrybut, który określa typ z Create metodą, która może to zrobić.
Biorąc pod uwagę następujący Objective-C kod:
@interface ObjCBlockTester : NSObject {
}
-(void) classCallback: (void (^)())completionHandler;
-(void) callClassCallback;
@end
@implementation ObjCBlockTester
-(void) classCallback: (void (^)())completionHandler
{
}
-(void) callClassCallback
{
[self classCallback: ^()
{
NSLog (@"called!");
}];
}
@end
oraz następujący kod powiązania:
[BaseType (typeof (NSObject))]
interface ObjCBlockTester
{
[Export ("classCallback:")]
void ClassCallback (Action completionHandler);
}
generator będzie produkować:
[Register("ObjCBlockTester", true)]
public unsafe partial class ObjCBlockTester : NSObject {
// unrelated code...
[Export ("callClassCallback")]
[BindingImpl (BindingImplOptions.GeneratedCode | BindingImplOptions.Optimizable)]
public virtual void CallClassCallback ()
{
if (IsDirectBinding) {
ApiDefinition.Messaging.void_objc_msgSend (this.Handle, Selector.GetHandle ("callClassCallback"));
} else {
ApiDefinition.Messaging.void_objc_msgSendSuper (this.SuperHandle, Selector.GetHandle ("callClassCallback"));
}
}
[Export ("classCallback:")]
[BindingImpl (BindingImplOptions.GeneratedCode | BindingImplOptions.Optimizable)]
public unsafe virtual void ClassCallback ([BlockProxy (typeof (Trampolines.NIDActionArity1V0))] System.Action completionHandler)
{
// ...
}
}
static class Trampolines
{
[UnmanagedFunctionPointerAttribute (CallingConvention.Cdecl)]
[UserDelegateType (typeof (System.Action))]
internal delegate void DActionArity1V0 (IntPtr block);
static internal class SDActionArity1V0 {
static internal readonly DActionArity1V0 Handler = Invoke;
[MonoPInvokeCallback (typeof (DActionArity1V0))]
static unsafe void Invoke (IntPtr block) {
var descriptor = (BlockLiteral *) block;
var del = (System.Action) (descriptor->Target);
if (del != null)
del (obj);
}
}
internal class NIDActionArity1V0 {
IntPtr blockPtr;
DActionArity1V0 invoker;
[Preserve (Conditional=true)]
[BindingImpl (BindingImplOptions.GeneratedCode | BindingImplOptions.Optimizable)]
public unsafe NIDActionArity1V0 (BlockLiteral *block)
{
blockPtr = _Block_copy ((IntPtr) block);
invoker = block->GetDelegateForBlock<DActionArity1V0> ();
}
[Preserve (Conditional=true)]
[BindingImpl (BindingImplOptions.GeneratedCode | BindingImplOptions.Optimizable)]
~NIDActionArity1V0 ()
{
_Block_release (blockPtr);
}
[Preserve (Conditional=true)]
[BindingImpl (BindingImplOptions.GeneratedCode | BindingImplOptions.Optimizable)]
public unsafe static System.Action Create (IntPtr block)
{
if (block == IntPtr.Zero)
return null;
if (BlockLiteral.IsManagedBlock (block)) {
var existing_delegate = ((BlockLiteral *) block)->Target as System.Action;
if (existing_delegate != null)
return existing_delegate;
}
return new NIDActionArity1V0 ((BlockLiteral *) block).Invoke;
}
[Preserve (Conditional=true)]
[BindingImpl (BindingImplOptions.GeneratedCode | BindingImplOptions.Optimizable)]
unsafe void Invoke ()
{
invoker (blockPtr);
}
}
}
W przypadku Objective-C wywołań [ObjCBlockTester callClassCallback]środowisko uruchomieniowe Xamarin.iOS/Xamarin.Mac przyjrzy się atrybutowi [BlockProxy (typeof (Trampolines.NIDActionArity1V0))] parametru . Następnie wyszuka metodę Create dla tego typu i wywoła tę metodę, aby utworzyć delegata.
Ta optymalizacja znajdzie metodę Create w czasie kompilacji, a statyczny registrar wygeneruje kod, który wyszukuje metodę w czasie wykonywania przy użyciu tokenów metadanych przy użyciu atrybutu i odbicia (jest to znacznie szybsze, a także umożliwia konsolidatorowi usunięcie odpowiedniego kodu środowiska uruchomieniowego, dzięki czemu aplikacja jest mniejsza).
Jeśli program mmp/mtouch nie może odnaleźć Create metody, zostanie wyświetlone ostrzeżenie MT4174/MM4174, a wyszukiwanie zostanie wykonane w czasie wykonywania.
Najbardziej prawdopodobną przyczyną jest ręcznie napisany kod powiązania bez wymaganych [BlockProxy] atrybutów.
Ta optymalizacja wymaga włączenia statycznego registrar .
Jest ona zawsze włączona domyślnie (o ile jest włączona funkcja statyczna registrar ).
Domyślne zachowanie można przesłonić, przekazując --optimize=[+|-]static-delegate-to-block-lookup do mtouch/mmp.