Xamarin の型レジストラー。 iOSType registrar for Xamarin.iOS

このドキュメントでは、Xamarin. iOS によって使用されるタイプ登録システムについて説明します。This document describes the type registration system used by Xamarin.iOS.

マネージクラスとマネージメソッドの登録Registration of managed classes and methods

起動時に、Xamarin. iOS によって登録されます。During startup, Xamarin.iOS will register:

  • [Register] 属性を持つクラスは、Objective-C クラスとして使用されます。Classes with a [Register] attribute as Objective-C classes.
  • [Category] 属性を持つクラスは、Objective-C カテゴリとして使用されます。Classes with a [Category] attribute as Objective-C categories.
  • " Protocol " 属性を持つインターフェイスを目的とする C プロトコルとして使用します。Interfaces with a [Protocol] attribute as Objective-C protocols.
  • [Export]が設定されたメンバーは、Objective-C からアクセスすることができます。Members with an [Export], making it possible for Objective-C to access them.

たとえば、Xamarin. iOS アプリケーションで一般的に使用されるマネージ Main 方法を考えてみます。For example, consider the managed Main method common in Xamarin.iOS applications:

UIApplication.Main (args, null, "AppDelegate");

このコードは、AppDelegate という名前の型をアプリケーションのデリゲートクラスとして使用するように、C ランタイムに指示します。This code tells the Objective-C runtime to use the type called AppDelegate as the application's delegate class. 目的 C ランタイムでC#AppDelegateクラスのインスタンスを作成できるようにするには、そのクラスを登録する必要があります。For the Objective-C runtime to be able to create an instance of the C# AppDelegate class, that class must be registered.

Xamarin iOS は、実行時 (動的登録) またはコンパイル時 (静的登録) で、自動的に登録を実行します。Xamarin.iOS performs registration automatically, either at runtime (dynamic registration) or at compile time (static registration).

動的登録では、起動時にリフレクションを使用して、登録するすべてのクラスとメソッドを検索し、それらを目的の C ランタイムに渡します。Dynamic registration uses reflection at startup to find all the classes and methods to register, passing them to the Objective-C runtime. 既定では、シミュレータービルドに動的登録が使用されます。Dynamic registration is used by default for simulator builds.

静的登録では、コンパイル時に、アプリケーションによって使用されるアセンブリが検査されます。Static registration inspects, at compile time, the assemblies used by the application. これは、目的の C に登録するクラスとメソッドを決定し、バイナリに埋め込まれているマップを生成します。It determines the classes and methods to register with Objective-C and generates a map, which is embedded into your binary. 次に、スタートアップ時に、マップを目的の C ランタイムに登録します。Then, at startup, it registers the map with the Objective-C runtime. デバイスのビルドには、静的登録が使用されます。Static registration is used for device builds.

カテゴリCategories

Xamarin. iOS 8.10 以降では、構文を使用してC# 、目的の C のカテゴリを作成することができます。Starting with Xamarin.iOS 8.10, it is possible to create Objective-C categories using C# syntax.

カテゴリを作成するには、[Category] 属性を使用して、拡張する型を指定します。To create a category, use the [Category] attribute and specify the type to extend. たとえば、次のコードは NSStringを拡張します。For example, the following code extends NSString:

[Category (typeof (NSString))]

各カテゴリのメソッドには、[Export] 属性があるため、目的の C ランタイムで使用できます。Each of a category's methods has an [Export] attribute, making it available to the Objective-C runtime:

[Export ("today")]
public static string Today ()
{
    return "Today";
}

すべてのマネージ拡張メソッドは静的である必要がありますが、拡張メソッドの標準C#構文を使用して、目的の C インスタンスメソッドを作成することができます。All managed extension methods must be static, but it’s possible to create Objective-C instance methods using the standard C# syntax for extension methods:

[Export ("toUpper")]
public static string ToUpper (this NSString self)
{
    return self.ToString ().ToUpper ();
}

拡張メソッドの最初の引数は、メソッドが呼び出されたインスタンスです。The first argument to the extension method is the instance on which the method was invoked:

[Category (typeof (NSString))]
public static class MyStringCategory
{
    [Export ("toUpper")]
    static string ToUpper (this NSString self)
    {
        return self.ToString ().ToUpper ();
    }
 }

この例では、NSString クラスにネイティブ toUpper インスタンスメソッドを追加します。This example will add a native toUpper instance method to the NSString class. このメソッドは、次のように、目的の C から呼び出すことができます。This method can be called from Objective-C:

[Category (typeof (UIViewController))]
public static class MyViewControllerCategory
{
    [Export ("shouldAutoRotate")]
    static bool GlobalRotate ()
    {
        return true;
    }
}

プロトコルProtocols

Xamarin. iOS 8.10 以降では、[Protocol] 属性を持つインターフェイスが、プロトコルとして目的の C にエクスポートされます。Starting with Xamarin.iOS 8.10, interfaces with the [Protocol] attribute will be exported to Objective-C as protocols:

[Protocol ("MyProtocol")]
interface IMyProtocol
{
    [Export ("method")]
    void Method ();
}

class MyClass : IMyProtocol
{
    void Method ()
    {
    }
}

このコードでは、IMyProtocolMyProtocol と呼ばれるプロトコルとして、またプロトコルを実装する MyClass と呼ばれるクラスにエクスポートします。This code exports IMyProtocol to Objective-C as a protocol called MyProtocol and a class called MyClass that implements the protocol.

新しい登録システムNew registration system

安定した6.2.6 バージョンとベータ版6.3.4 バージョンから、新しい静的レジストラーが追加されました。Starting with the stable 6.2.6 version and the beta 6.3.4 version, we've added a new static registrar. 7.2.1 バージョンでは、新しいレジストラーが既定値として作成されました。In the 7.2.1 version, we made the new registrar the default.

この新しい登録システムには、次の新機能が用意されています。This new registration system offers the following new features:

  • コンパイル時のプログラマエラーの検出:Compile-time detection of programmer errors:

    • 2つのクラスが同じ名前で登録されています。Two classes being registered with the same name.
    • 同じセレクターに応答するために複数のメソッドがエクスポートされていますMore than one method exported to respond to the same selector
  • 未使用のネイティブコードの削除:Removal of unused native code:

    • 新しい登録システムは、スタティックライブラリで使用されるコードへの強い参照を追加します。これにより、ネイティブリンカーは、結果として得られるバイナリから未使用のネイティブコードを取り除くことができます。The new registration system will add strong references to code used in static libraries, allowing the native linker to strip out unused native code from the resulting binary. Xamarin のサンプルバインドでは、ほとんどのアプリケーションが少なくとも300k より小さくなります。On Xamarin's sample bindings, most applications become at least 300k smaller.
  • NSObjectの汎用サブクラスのサポート詳細については、「 NSObject ジェネリック」を参照してください。Support for generic subclasses of NSObject; see NSObject Generics for more information. さらに、新しい登録システムは、以前は実行時にランダムな動作を引き起こした、サポートされていないジェネリックコンストラクトをキャッチします。Additionally the new registration system will catch unsupported generic constructs which would previously have caused random behavior at runtime.

新しいレジストラーでキャッチされたエラーErrors caught by the new registrar

次に、新しいレジストラーでキャッチされるエラーの例をいくつか示します。Below are some examples of the errors caught by the new registrar.

  • 同じクラス内で同じセレクターを複数回エクスポートする場合:Exporting the same selector more than once in the same class:

    [Register]
    class MyDemo : NSObject
    {
        [Export ("foo:")]
        void Foo (NSString str);
        [Export ("foo:")]
        void Foo (string str)
    }
    
  • 同じ目的 C 名で複数のマネージクラスをエクスポートする場合:Exporting more than one managed class with the same Objective-C name:

    [Register ("Class")]
    class MyClass : NSObject {}
    
    [Register ("Class")]
    class YourClass : NSObject {}
    
  • ジェネリックメソッドのエクスポート:Exporting generic methods:

    [Register]
    class MyDemo : NSObject
    {
        [Export ("foo")]
        void Foo<T> () {}
    }
    

新しいレジストラーの制限事項Limitations of the new registrar

新しいレジストラーについては、次の点に注意してください。Some things to keep in mind about the new registrar:

  • 新しい登録システムで動作するように、一部のサードパーティライブラリを更新する必要があります。Some third-party libraries must be updated to work with the new registration system. 詳細については、以下の「必要な変更」を参照してください。See required modifications below for more details.

  • また、アカウントフレームワークが使用されている場合、Clang を使用する必要があるという点もあります (これは、Apple のAccounts. hヘッダーは clang でのみコンパイルできるためです)。A short-term downside is also that Clang must be used if the Accounts framework is used (this is because Apple's accounts.h header can only be compiled by Clang). Xcode 4.6 以前を使用している場合は、追加の mtouch 引数に --compiler:clang を追加して Clang を使用します (Xamarin は Xcode 5.0 以降で Clang を自動的に選択します)。Add --compiler:clang to the additional mtouch arguments to use Clang if you're using Xcode 4.6 or earlier (Xamarin.iOS will automatically select Clang in Xcode 5.0 or later.)

  • エクスポートされた型名に ASCII 以外の文字が含まれている場合、Xcode 4.6 (またはそれ以前) が使用されている場合は、GCC/G + + を選択する必要があります (これは、Xcode 4.6 に付属する Clang のバージョンでは、目的の C コードでは、識別子に含まれる ASCII 以外の文字をサポートIf Xcode 4.6 (or earlier) is used, GCC/G++ must be selected if exported type names contain non-ASCII characters (this is because the version of Clang shipped with Xcode 4.6 does not support non-ASCII characters inside identifiers in Objective-C code). 追加の mtouch 引数に --compiler:gcc を追加して、GCC を使用します。Add --compiler:gcc to the additional mtouch arguments to use GCC.

レジストラーの選択Selecting a registrar

次のいずれかのオプションをプロジェクトのIOS ビルド設定の追加の mtouch 引数に追加することで、別のレジストラーを選択できます。You can select a different registrar by adding one of the following options to the additional mtouch arguments in the project's iOS Build settings:

  • --registrar:static-デバイスビルドの既定値--registrar:static – default for device builds
  • --registrar:dynamic-シミュレータービルドの既定値--registrar:dynamic – default for simulator builds

注意

Xamarin の Classic API では、--registrar:legacystatic--registrar:legacydynamicなどの他のオプションがサポートされています。Xamarin's Classic API supported other options such as --registrar:legacystatic and --registrar:legacydynamic. ただし、これらのオプションは、Unified API ではサポートされていません。However, these options are not supported by the Unified API.

古い登録システムの欠点Shortcomings in the old registration system

以前の登録システムには、次のような欠点があります。The old registration system has the following drawbacks:

  • サードパーティのネイティブライブラリでは、目的 C のクラスおよびメソッドへの (ネイティブな) 静的参照がありませんでした。これは、実際には使用されていなかったサードパーティのネイティブコード (すべてが削除されるため) を削除するようネイティブリンカーに要求できなかったことを意味します。There was no (native) static reference to Objective-C classes and methods in third-party native libraries, which meant that we couldn't ask the native linker to remove third-party native code that wasn't actually used (because everything would be removed). これは、すべてのサードパーティバインドが実行する必要があった -force_load libNative.a (または、[LinkWith] 属性の同等の ForceLoad=true) のためです。This is the reason for the -force_load libNative.a that every third-party binding had to do (or the equivalent ForceLoad=true in the [LinkWith] attribute).
  • 同じ目的 C 名を持つ2つのマネージ型を、警告なしでエクスポートできます。You could export two managed types with the same Objective-C name with no warning. まれに、異なる名前空間に2つの AppDelegate クラスがあるというシナリオがあります。A rare scenario was to end up with two AppDelegate classes in different namespaces. 実行時には、選択されたものが完全にランダムになります (実際には、非常に不可解でストレスのあるデバッグエクスペリエンスを実現するために再構築されていないアプリの実行間で異なります)。At runtime it would be completely random which one was picked (in fact, it varied between runs of an app that wasn't even rebuilt - which made for a very puzzling and frustrating debugging experience).
  • 同じ目的 C シグネチャを使用して2つのメソッドをエクスポートできます。You could export two methods with the same Objective-C signature. ここでも、前の例と同じように、この問題はランダムに呼び出されていましたが、この問題は前の例ほど一般的ではありませんでしたが、ほとんどの場合、このバグを実際に体験する唯一の方法は unlucky マネージメソッドをオーバーライドすることでした。Yet again which one would be called from Objective-C was random (but this problem wasn't as common as the previous one, mostly because the only way to actually experience this bug was to override the unlucky managed method).
  • エクスポートされたメソッドのセットは、動的ビルドと静的ビルドで若干異なりました。The set of methods that was exported was slightly different between dynamic and static builds.
  • ジェネリッククラスのエクスポート時には正しく機能しません (実行時に実行される厳密なジェネリック実装はランダムで、実質的には動作が不定になります)。It does not work properly when exporting generic classes (which exact generic implementation executed at runtime would be random, effectively resulting in undetermined behavior).

新しいレジストラー: バインドに必要な変更New registrar: required changes to bindings

このセクションでは、新しいレジストラーを使用するために行う必要があるバインドの変更について説明します。This section describes bindings changes that must be made in order to work with the new registrar.

プロトコルには [Protocol] 属性が必要ですProtocols must have the [Protocol] attribute

プロトコルには、[Protocol] 属性を設定する必要があります。Protocols must now have the [Protocol] attribute. これを行わないと、次のようなネイティブリンカーエラーが発生します。If you do not do this, you will a native linker error such as:

Undefined symbols for architecture i386: "_OBJC_CLASS_$_ProtocolName", referenced from: ...

セレクターには、有効な数のパラメーターが必要ですSelectors must have a valid number of parameters

すべてのセレクターは、パラメーターの数を正確に示す必要があります。All selectors must indicate number of parameters correctly. 以前は、これらのエラーは無視され、実行時の問題が発生する可能性がありました。Previously, these errors were ignored and could cause runtime problems.

つまり、コロンの数はパラメーターの数と一致している必要があります。In short, the number of colons must match the number of parameters:

  • パラメーターがありません: fooNo parameters: foo
  • 1つのパラメーター: foo:One parameter: foo:
  • 2つのパラメーター: foo:parameterName2:Two parameters: foo:parameterName2:

不適切な使用方法を次に示します。The following are incorrect uses:

// Invalid: export takes no arguments, but function expects one
[Export ("apply")]
void Apply (NSObject target);

// Invalid: exported as taking an argument, but the managed version does not have one:
[Export ("display:")]
void Display ();

エクスポートで IsVariadic パラメーターを使用するUse IsVariadic parameter in Export

可変個引数関数は、[Export] 属性に IsVariadic 引数を使用する必要があります。Variadic functions must use the IsVariadic argument to the [Export] attribute:

[Export ("variadicMethod:", IsVariadic = true)]
void VariadicMethod (NSObject first, IntPtr subsequent);

ネイティブライブラリに存在しないクラスをバインドすることはできません。It is impossible to bind classes that don't exist in the native library. ネイティブライブラリでクラスを削除または名前変更した場合は、一致するようにバインドを更新してください。If a class has been removed from or renamed in the native library, make sure to update the bindings to match.