Aracılığıyla paylaş


Kitaplıkları bağlama Objective-C

Xamarin.iOS veya Xamarin.Mac ile çalışırken üçüncü taraf Objective-C kitaplığını kullanmak istediğiniz durumlarla karşılaşabilirsiniz. Bu gibi durumlarda, yerel Objective-C kitaplıklara C# bağlaması oluşturmak için Xamarin Bağlama Projelerini kullanabilirsiniz. Proje, iOS ve Mac API'lerini C# 'ye getirmek için kullandığımız araçları kullanır.

Bu belgede API'lerin nasıl bağlandığı Objective-C açıklanır. Yalnızca C API'lerini bağlıyorsanız bunun için standart .NET mekanizması olan P/Invoke çerçevesini kullanmanız gerekir. C kitaplığını statik olarak bağlamayla ilgili ayrıntılar Yerel Kitaplıkları Bağlama sayfasında bulunabilir.

Yardımcı Bağlama Türleri Başvuru Kılavuzu'na bakın. Ayrıca, arka planda neler olduğu hakkında daha fazla bilgi edinmek istiyorsanız Bağlamaya Genel Bakış sayfamızı gözden geçirin.

Bağlamalar hem iOS hem de Mac kitaplıkları için oluşturulabilir. Bu sayfada iOS bağlaması üzerinde nasıl çalışıldığı açıklanır, ancak Mac bağlamaları çok benzerdir.

iOS için Örnek Kod

Bağlamaları denemek için iOS Bağlama Örneği projesini kullanabilirsiniz.

Başlarken

Bağlama oluşturmanın en kolay yolu Xamarin.iOS Bağlama Projesi oluşturmaktır. Bunu Mac için Visual Studio proje türü olan iOS > Kitaplık > Bağlamaları Kitaplığı'nı seçerek yapabilirsiniz:

Bunu Mac için Visual Studio proje türü olan iOS Kitaplık Bağlamaları Kitaplığı'nı seçerek yapın

Oluşturulan proje, düzenleyebileceğiniz küçük bir şablon içerir, iki dosya içerir: ApiDefinition.cs ve StructsAndEnums.cs.

ApiDefinition.cs, API sözleşmesini tanımlayacağınız yerdir; bu, temel alınan Objective-C API'nin C# içine nasıl yansıtıldığını açıklayan dosyadır. Bu dosyanın söz dizimi ve içeriği, bu belgenin ana tartışma konusudur ve içeriği C# arabirimleri ve C# temsilci bildirimleriyle sınırlıdır. Dosya StructsAndEnums.cs , arabirimler ve temsilciler tarafından gerekli olan tanımları gireceğiniz dosyadır. Bu, kodunuzun kullanabileceği numaralandırma değerlerini ve yapıları içerir.

API bağlama

Kapsamlı bir bağlama yapmak için API tanımını anlamak Objective-C ve .NET Framework Tasarım yönergelerini öğrenmek isteyeceksiniz.

Kitaplığınızı bağlamak için genellikle bir API tanım dosyasıyla başlarsınız. API tanım dosyası, bağlamayı yönlendirmeye yardımcı olan birkaç öznitelikle ek açıklama eklenmiş C# arabirimlerini içeren bir C# kaynak dosyasıdır. Bu dosya, C# ile Objective-C arasındaki sözleşmeyi tanımlayan dosyadır.

Örneğin, bu kitaplık için önemsiz bir api dosyasıdır:

using Foundation;

namespace Cocos2D {
  [BaseType (typeof (NSObject))]
  interface Camera {
    [Static, Export ("getZEye")]
    nfloat ZEye { get; }

    [Export ("restore")]
    void Restore ();

    [Export ("locate")]
    void Locate ();

    [Export ("setEyeX:eyeY:eyeZ:")]
    void SetEyeXYZ (nfloat x, nfloat y, nfloat z);

    [Export ("setMode:")]
    void SetMode (CameraMode mode);
  }
}

Yukarıdaki örnek, temel türden türetilen NSObject (bu tür) Foundation.NSObjectadlı ve statik bir özellik (ZEye), bağımsız değişken almayan iki yöntem ve üç bağımsız değişken alan bir yöntem tanımlayan bir Cocos2D.Camera sınıf tanımlar.

API dosyasının biçiminin ve kullanabileceğiniz özniteliklerin ayrıntılı bir açıklaması aşağıdaki API tanım dosyası bölümünde ele alınmıştır.

Tam bağlama oluşturmak için genellikle dört bileşenle ilgilenirsiniz:

  • API tanım dosyası (ApiDefinition.cs şablonda).
  • İsteğe bağlı: API tanım dosyasının (StructsAndEnums.cs şablonda) gerektirdiği tüm numaralandırmalar, türler, yapılar.
  • İsteğe bağlı: Oluşturulan bağlamayı genişletebilecek veya daha C# dostu bir API (projeye eklediğiniz tüm C# dosyaları) sağlayabilecek ek kaynaklar.
  • Bağladığınız yerel kitaplık.

Bu grafik, dosyalar arasındaki ilişkiyi gösterir:

Bu grafik, dosyalar arasındaki ilişkiyi gösterir

API Tanımı dosyası yalnızca ad alanlarını ve arabirim tanımlarını (bir arabirimin içerebileceği üyelerle birlikte) içerir ve sınıflar, numaralandırmalar, temsilciler veya yapılar içermemelidir. API tanım dosyası yalnızca API'yi oluşturmak için kullanılacak sözleşmedir.

Numaralandırmalar veya destekleyici sınıflar gibi ihtiyacınız olan ek kodlar ayrı bir dosyada barındırılmalıdır; yukarıdaki örnekte "Kamera Mode" CS dosyasında bulunmayan ve ayrı bir dosyada barındırılması gereken bir numaralandırma değeridir, örneğinStructsAndEnums.cs:

public enum CameraMode {
    FlyOver, Back, Follow
}

dosyası APIDefinition.cs sınıfıyla StructsAndEnum birleştirilir ve kitaplığın çekirdek bağlamasını oluşturmak için kullanılır. Sonuçta elde edilen kitaplığı olduğu gibi kullanabilirsiniz, ancak genellikle kullanıcılarınızın yararına bazı C# özellikleri eklemek için sonuçta elde edilen kitaplığı ayarlamak istersiniz. Bazı örnekler arasında yöntem ToString() uygulama, C# dizin oluşturucuları sağlama, bazı yerel türlere ve bu türlerden örtük dönüştürmeler ekleme veya bazı yöntemlerin kesin olarak türlenmiş sürümlerini sağlama sayılabilir. Bu geliştirmeler ek C# dosyalarında depolanır. Yalnızca projenize C# dosyalarını eklediğinizde bu derleme işlemine eklenirler.

Bu, kodu dosyanıza Extra.cs nasıl uygulayabileceğinizi gösterir. Ve çekirdek bağlamasının birleşiminden oluşturulan kısmi sınıfları artırdıkça kısmi sınıfları kullanacağınıza ApiDefinition.csStructsAndEnums.cs dikkat edin:

public partial class Camera {
    // Provide a ToString method
    public override string ToString ()
    {
         return String.Format ("ZEye: {0}", ZEye);
    }
}

Kitaplığı oluşturmak yerel bağlamanızı oluşturur.

Bu bağlamayı tamamlamak için projeye yerel kitaplığı eklemeniz gerekir. Yerel kitaplığı Bulucu'dan çözüm gezginindeki projeye sürükleyip bırakarak veya projeye sağ tıklayıp Yerel kitaplığı seçmek için Dosya Ekle'yi>seçerek projenize yerel kitaplığı ekleyerek bunu yapabilirsiniz. Kurala göre yerel kitaplıklar "lib" sözcüğüyle başlar ve ".a" uzantısıyla biter. Bunu yaptığınızda, Mac için Visual Studio iki dosya ekler: .a dosyası ve yerel kitaplığın içeriği hakkında bilgi içeren otomatik olarak doldurulmuş bir C# dosyası:

Kurala göre yerel kitaplıklar lib sözcüğüyle başlar ve .a uzantısıyla biter

Dosyanın içeriğinde bu kitaplığın libMagicChord.linkwith.cs nasıl kullanılabileceğini belirten bilgiler vardır ve IDE'nize bu ikili dosyayı sonuçta elde edilen DLL dosyasına paketlemesi talimatını vemektedir:

using System;
using ObjCRuntime;

[assembly: LinkWith ("libMagicChord.a", SmartLink = true, ForceLoad = true)]

nasıl kullanılacağı hakkında tüm ayrıntılar [LinkWith]özniteliği Bağlama Türleri Başvuru Kılavuzu'nda belgelenmiştir.

Şimdi projeyi oluşturduğunuzda hem bağlamayı hem de yerel kitaplığı içeren bir MagicChords.dll dosyayla sonuçlanırsınız. Bu projeyi veya sonuçta elde edilen DLL'yi kendi kullanımları için diğer geliştiricilere dağıtabilirsiniz.

Bazen birkaç numaralandırma değerine, temsilci tanımlarına veya diğer türlere ihtiyacınız olduğunu fark edebilirsiniz. Bu yalnızca bir sözleşme olduğundan bunları API tanımları dosyasına yerleştirmeyin

API tanım dosyası

API tanım dosyası bir dizi arabirimden oluşur. API tanımındaki arabirimler bir sınıf bildirimine dönüştürülür ve sınıfın temel sınıfını [BaseType] belirtmek için özniteliğiyle donatılmalıdır.

Sözleşme tanımı için arabirimler yerine neden sınıfları kullanmadığımız merak ediyor olabilirsiniz. API tanım dosyasında bir yöntem gövdesi sağlamak zorunda kalmadan veya özel durum oluşturması veya anlamlı bir değer döndürmesi gereken bir gövde sağlamak zorunda kalmadan bir yöntemin sözleşmesini yazmamıza olanak sağladığından arabirimleri seçtik.

Ancak arabirimini bir sınıf oluşturmak için iskelet olarak kullandığımız için, bağlamayı yönlendirmek için sözleşmenin çeşitli bölümlerini özniteliklerle süslemeye başvurmak zorunda kaldık.

Bağlama yöntemleri

Yapabileceğiniz en basit bağlama, bir yöntemi bağlamaktır. C# adlandırma kurallarıyla arabirimde bir yöntem bildirmesi ve yöntemini ile süslemesi gerekir [Export] özniteliği. [Export] özniteliği, C# adınızı Xamarin.iOS çalışma zamanındaki adla Objective-C ilişkilendiren özelliktir. parametresi [Export] özniteliği, seçicinin Objective-C adıdır. Bazı örnekler:

// A method, that takes no arguments
[Export ("refresh")]
void Refresh ();

// A method that takes two arguments and return the result
[Export ("add:and:")]
nint Add (nint a, nint b);

// A method that takes a string
[Export ("draw:atColumn:andRow:")]
void Draw (string text, nint column, nint row);

Yukarıdaki örneklerde örnek yöntemlerini nasıl bağlayabileceğiniz gösterilmektedir. Statik yöntemleri bağlamak için aşağıdaki gibi özniteliğini [Static] kullanmanız gerekir:

// A static method, that takes no arguments
[Static, Export ("beep")]
void Beep ();

Bu, sözleşme bir arabirimin parçası olduğundan ve arabirimlerin statik ve örnek bildirimleriyle ilgili bir fikri olmadığından, özniteliklere başvurmak için bir kez daha gereklidir. Bağlamadan belirli bir yöntemi gizlemek istiyorsanız, yöntemini özniteliğiyle [Internal] süsleyebilirsiniz.

Komutu, başvuru parametrelerinin btouch-native null olmaması için denetimler ekler. Belirli bir parametre için null değerlere izin vermek istiyorsanız [NullAllowed] parametresindeki özniteliği, örneğin:

[Export ("setText:")]
string SetText ([NullAllowed] string text);

Bir başvuru türünü dışarı aktarırken, anahtar sözcüğüyle [Export] ayırma semantiğini de belirtebilirsiniz. Bu, veri sızdırıldığından emin olmak için gereklidir.

Bağlama özellikleri

Yöntemlerde olduğu gibi, Objective-C özellikler de [Export] özniteliğini seçin ve doğrudan C# özelliklerine eşleyin. Aynı yöntemler gibi özellikler de [Static] ve [Internal] Öznitelik.

btouch-native kapaklarının altındaki bir özellikte özniteliğini kullandığınızda [Export] aslında iki yöntem bağlanır: alıcı ve ayarlayıcı. Dışarı aktarmak için sağladığınız ad basename'dir ve ayarlayıcı "set" sözcüğünü önceden koyarak hesaplanır, temel adın ilk harfi büyük harfe dönüştürülür ve seçici bağımsız değişken alır. Bu, [Export ("label")] bir özelliğe uygulananın aslında "label" ve "setLabel:" Objective-C yöntemlerini bağladığını gösterir.

Objective-C Bazen özellikler yukarıda açıklanan desene uymaz ve adın üzerine el ile yazılır. Bu gibi durumlarda bağlamanın oluşturulma şeklini denetlemek için [Bind] örneğin, alıcı veya ayarlayıcıdaki özniteliği:

[Export ("menuVisible")]
bool MenuVisible { [Bind ("isMenuVisible")] get; set; }

Bu, "isMenuVisible" ve "setMenuVisible:" değerlerini bağlar. İsteğe bağlı olarak, bir özellik aşağıdaki söz dizimi kullanılarak bağlanabilir:

[Category, BaseType(typeof(UIView))]
interface UIView_MyIn
{
  [Export ("name")]
  string Name();

  [Export("setName:")]
  void SetName(string name);
}

Burada alıcı ve ayarlayıcı açıkça yukarıdaki ve setName bağlamalarında name olarak tanımlanır.

kullanarak [Static]statik özellikleri desteklemeye ek olarak, iş parçacığı statik özelliklerini ile [IsThreadStatic]süsleyebilirsiniz, örneğin:

[Export ("currentRunLoop")][Static][IsThreadStatic]
NSRunLoop Current { get; }

Yöntemlerin bazı parametrelerin ile [NullAllowed]işaretlenmesini sağlaması gibi, uygulayabilirsiniz [NullAllowed] null değerinin özellik için geçerli bir değer olduğunu belirtmek için bir özelliğe, örneğin:

[Export ("text"), NullAllowed]
string Text { get; set; }

[NullAllowed] Parametre doğrudan ayarlayıcıda da belirtilebilir:

[Export ("text")]
string Text { get; [NullAllowed] set; }

Özel denetimleri bağlama uyarıları

Özel denetim için bağlama ayarlanırken aşağıdaki uyarılar dikkate alınmalıdır:

  1. Bağlama özellikleri statik olmalıdır - Özelliklerin bağlamasını tanımlarken özniteliği [Static] kullanılmalıdır.
  2. Özellik adları tam olarak eşleşmelidir - Özelliği bağlamak için kullanılan ad, özel denetimdeki özelliğin adıyla tam olarak eşleşmelidir.
  3. Özellik türleri tam olarak eşleşmelidir - Özelliği bağlamak için kullanılan değişken türü, özel denetimdeki özelliğin türüyle tam olarak eşleşmelidir.
  4. Kesme noktaları ve getter/setter - Özelliğin alıcı veya ayarlayıcı yöntemlerine yerleştirilen kesme noktalarına hiçbir zaman ulaşılmayacaktır.
  5. Geri Çağırmaları Gözlemle - Özel denetimlerin özellik değerlerindeki değişikliklerden haberdar olmak için gözlem geri çağırmalarını kullanmanız gerekir.

Yukarıdaki listelenen uyarılardan herhangi birinin gözlemlenmemesi, bağlamanın çalışma zamanında sessizce başarısız olmasıyla sonuçlanabilir.

Objective-C değiştirilebilir desen ve özellikler

Objective-C çerçeveler, bazı sınıfların değiştirilebilir bir alt sınıfla sabit olduğu bir deyim kullanır. Örneğin NSString , değişmez sürüm NSMutableString , mutasyona izin veren alt sınıftır.

Bu sınıflarda sabit temel sınıfın bir alıcısı olan ancak ayarlayıcı içermeyen özellikler içerdiğini görmek yaygın bir durumdur. Ve değiştirilebilir sürüm için ayarlayıcıyı tanıtmak için. Bu C# ile gerçekten mümkün olmadığından, bu deyimi C# ile çalışacak bir deyimle eşlemek zorunda kaldık.

Bunun C# ile eşlenme yöntemi, hem alıcıyı hem de ayarlayıcıyı temel sınıfa eklemek, ancak ayarlayıcıyı bir ile işaretlemektir [NotImplemented] özniteliği.

Ardından, değiştirilebilir alt sınıfında [Override] özniteliğini kullanarak özelliğin üst öğe davranışını geçersiz kıldığından emin olun.

Örnek:

[BaseType (typeof (NSObject))]
interface MyTree {
    string Name { get; [NotImplemented] set; }
}

[BaseType (typeof (MyTree))]
interface MyMutableTree {
    [Override]
    string Name { get; set; }
}

Bağlama oluşturucuları

Araç btouch-native , sınıfınızda otomatik olarak fours oluşturucuları oluşturur ve belirli bir sınıf Fooiçin şunları oluşturur:

  • Foo (): varsayılan oluşturucu ('in "init" oluşturucusunda eşler Objective-C)
  • Foo (NSCoder): NIB dosyalarının seri durumdan çıkarılması sırasında kullanılan oluşturucu ('nin "initWithCoder:" oluşturucusunda eşlenir Objective-C).
  • Foo (IntPtr handle): tanıtıcı tabanlı oluşturma için oluşturucu, çalışma zamanının yönetilmeyen bir nesneden yönetilen bir nesneyi kullanıma sunması gerektiğinde çalışma zamanı tarafından çağrılır.
  • Foo (NSEmptyFlag): Çift başlatmayı önlemek için türetilmiş sınıflar tarafından kullanılır.

Tanımladığınız oluşturucular için Arabirim tanımının içinde aşağıdaki imza kullanılarak bildirilmesi gerekir: bir IntPtr değer döndürmeleri ve yöntemin adı Oluşturucu olmalıdır. Örneğin oluşturucuyu initWithFrame: bağlamak için aşağıdakini kullanabilirsiniz:

[Export ("initWithFrame:")]
IntPtr Constructor (CGRect frame);

Bağlama protokolleri

API tasarım belgesinde açıklandığı gibi, Modeller ve Protokoller'i ele alan bölümde, Xamarin.iOS protokolleri ile bayrak eklenmiş sınıflara eşler Objective-C[Model] özniteliği. Bu genellikle temsilci sınıfları uygulanırken Objective-C kullanılır.

Normal bir bağlı sınıf ile temsilci sınıfı arasındaki büyük fark, temsilci sınıfının bir veya daha fazla isteğe bağlı yöntemi olmasıdır.

Örneğin, sınıfını UIKitUIAccelerometerDelegategöz önünde bulundurun: Xamarin.iOS'ta bu şekilde bağlanır:

[BaseType (typeof (NSObject))]
[Model][Protocol]
interface UIAccelerometerDelegate {
        [Export ("accelerometer:didAccelerate:")]
        void DidAccelerate (UIAccelerometer accelerometer, UIAcceleration acceleration);
}

Bu, tanımında UIAccelerometerDelegate isteğe bağlı bir yöntem olduğundan, yapılacak başka bir şey yoktur. Ancak protokolde gerekli bir yöntem varsa, [Abstract] özniteliğini seçin. Bu, uygulamanın kullanıcısını yöntemi için bir gövde sağlamaya zorlar.

Genel olarak, protokoller iletilere yanıt veren sınıflarda kullanılır. Bu genellikle 'de Objective-C yapılır ve "delegate" özelliğine protokoldeki yöntemlere yanıt veren bir nesnenin örneği atanır.

Xamarin.iOS'taki kural, hem bir örneğinin Objective-CNSObject temsilciye atanabileceği gevşek bir şekilde bağlanmış stili desteklemek hem de bunun kesin olarak belirlenmiş bir sürümünü kullanıma sunmadır. Bu nedenle, genellikle hem kesin olarak türü belirlenmiş bir Delegate özellik hem de gevşek bir şekilde yazılan bir WeakDelegate özellik sağlarız. Genellikle gevşek türü belirlenmiş sürümü ile [Export]bağlarız ve kesin olarak türü belirlenmiş sürümü sağlamak için özniteliğini kullanırız [Wrap] .

Bu, sınıfı nasıl bağlayacağımızı UIAccelerometer gösterir:

[BaseType (typeof (NSObject))]
interface UIAccelerometer {
        [Static] [Export ("sharedAccelerometer")]
        UIAccelerometer SharedAccelerometer { get; }

        [Export ("updateInterval")]
        double UpdateInterval { get; set; }

        [Wrap ("WeakDelegate")]
        UIAccelerometerDelegate Delegate { get; set; }

        [Export ("delegate", ArgumentSemantic.Assign)][NullAllowed]
        NSObject WeakDelegate { get; set; }
}

MonoTouch 7.0'daki yenilikler

MonoTouch 7.0'dan itibaren yeni ve geliştirilmiş bir protokol bağlama işlevi birleştirildi. Bu yeni destek, belirli bir sınıfta bir veya daha fazla protokolü benimsemek için deyimlerin kullanılmasını Objective-C kolaylaştırır.

içindeki Objective-Cher protokol tanımı MyProtocol için artık IMyProtocol protokoldeki tüm gerekli yöntemlerin yanı sıra isteğe bağlı tüm yöntemleri sağlayan bir uzantı sınıfını listeleyen bir arabirim vardır. Yukarıdaki, Xamarin Studio düzenleyicisindeki yeni destekle birlikte geliştiricilerin önceki soyut model sınıflarının ayrı alt sınıflarını kullanmak zorunda kalmadan protokol yöntemlerini uygulamasına olanak tanır.

özniteliğini [Protocol] içeren herhangi bir tanım, protokolleri kullanma yönteminizi büyük ölçüde geliştiren üç destekleyici sınıf oluşturur:

// Full method implementation, contains all methods
class MyProtocol : IMyProtocol {
    public void Say (string msg);
    public void Listen (string msg);
}

// Interface that contains only the required methods
interface IMyProtocol: INativeObject, IDisposable {
    [Export ("say:")]
    void Say (string msg);
}

// Extension methods
static class IMyProtocol_Extensions {
    public static void Optional (this IMyProtocol this, string msg);
    }
}

Sınıf uygulaması, tek tek yöntemlerini geçersiz kılabileceğiniz ve tam tür güvenliği alabileceğiniz eksiksiz bir soyut sınıf sağlar. Ancak C# birden çok devralmayı desteklemediğinden, farklı bir temel sınıfa sahip olmanız gerekebilecek ancak yine de bir arabirim uygulamak istediğiniz senaryolar vardır.

Oluşturulan arabirim tanımı gelir. Protokolden tüm gerekli yöntemleri içeren bir arabirimdir. Bu, protokolünüzü uygulamak isteyen geliştiricilerin yalnızca arabirimi uygulamasına olanak tanır. Çalışma zamanı türü otomatik olarak protokolü benimseyerek kaydeder.

Arabirimin yalnızca gerekli yöntemleri listelediğini ve isteğe bağlı yöntemleri kullanıma sunar. Bu, protokolü benimseyen sınıfların gerekli yöntemler için tam tür denetimine sahip olacağı, ancak isteğe bağlı protokol yöntemleri için zayıf yazma (öznitelikleri kullanarak [Export] ve imzayı eşleştirerek el ile) başvurması gerektiği anlamına gelir.

Bağlama aracı, protokol kullanan bir API'yi kullanmayı kolaylaştırmak için isteğe bağlı tüm yöntemleri kullanıma sunan bir uzantı yöntemi sınıfı da oluşturur. Bu, BIR API kullandığınız sürece protokolleri tüm yöntemlere sahip olarak değerlendirebilmeniz anlamına gelir.

API'nizde protokol tanımlarını kullanmak istiyorsanız API tanımınıza boş iskelet arabirimleri yazmanız gerekir. MyProtocol'unu bir API'de kullanmak istiyorsanız, bunu yapmanız gerekir:

[BaseType (typeof (NSObject))]
[Model, Protocol]
interface MyProtocol {
    // Use [Abstract] when the method is defined in the @required section
    // of the protocol definition in Objective-C
    [Abstract]
    [Export ("say:")]
    void Say (string msg);

    [Export ("listen")]
    void Listen ();
}

interface IMyProtocol {}

[BaseType (typeof(NSObject))]
interface MyTool {
    [Export ("getProtocol")]
    IMyProtocol GetProtocol ();
}

Yukarıdakiler gereklidir çünkü bağlama zamanında IMyProtocol mevcut olmaz, bu nedenle boş bir arabirim sağlamanız gerekir.

Protokol tarafından oluşturulan arabirimleri benimseme

Protokoller için oluşturulan arabirimlerden birini her uyguladığınızda, aşağıdaki gibi:

class MyDelegate : NSObject, IUITableViewDelegate {
    nint IUITableViewDelegate.GetRowHeight (nint row) {
        return 1;
    }
}

Gerekli arabirim yöntemlerinin uygulaması uygun adla dışarı aktarılır, bu nedenle şuna eşdeğerdir:

class MyDelegate : NSObject, IUITableViewDelegate {
    [Export ("getRowHeight:")]
    nint IUITableViewDelegate.GetRowHeight (nint row) {
        return 1;
    }
}

Bu, tüm gerekli protokol üyeleri için çalışır, ancak dikkat edilmesi gereken isteğe bağlı seçiciler içeren özel bir durum vardır. İsteğe bağlı protokol üyeleri, temel sınıf kullanılırken aynı şekilde değerlendirilir:

public class UrlSessionDelegate : NSUrlSessionDownloadDelegate {
	public override void DidWriteData (NSUrlSession session, NSUrlSessionDownloadTask downloadTask, long bytesWritten, long totalBytesWritten, long totalBytesExpectedToWrite)

ancak protokol arabirimini kullanırken [Dışarı Aktar] öğesini eklemek gerekir. IDE, geçersiz kılma ile başlayarak eklediğinizde otomatik tamamlama yoluyla ekler.

public class UrlSessionDelegate : NSObject, INSUrlSessionDownloadDelegate {
	[Export ("URLSession:downloadTask:didWriteData:totalBytesWritten:totalBytesExpectedToWrite:")]
	public void DidWriteData (NSUrlSession session, NSUrlSessionDownloadTask downloadTask, long bytesWritten, long totalBytesWritten, long totalBytesExpectedToWrite)

Çalışma zamanında ikisi arasında küçük bir davranış farkı vardır.

  • Temel sınıfın kullanıcıları (örnekte NSUrlSessionDownloadDelegate), tüm gerekli ve isteğe bağlı seçicileri sağlayarak makul varsayılan değerler döndürür.
  • Arabirimin kullanıcıları (örnekte INSUrlSessionDownloadDelegate) yalnızca sağlanan seçicileri tam olarak yanıtlar.

Bazı nadir sınıflar burada farklı davranabilir. Hemen hemen her durumda, ancak her ikisini de kullanmak güvenlidir.

Sınıf uzantılarını bağlama

Içinde Objective-C C# uzantı yöntemlerine benzer şekilde yeni yöntemlerle sınıfları genişletmek mümkündür. Bu yöntemlerden biri mevcut olduğunda [BaseType] özniteliğini kullanarak yöntemi iletinin alıcısı olarak bayrak ekleyin Objective-C .

Örneğin, Xamarin.iOS'ta, içinde yöntem olarak içeri aktarıldığında UIKit tanımlanan NSString uzantı yöntemlerini NSStringDrawingExtensionsbağladık:

[Category, BaseType (typeof (NSString))]
interface NSStringDrawingExtensions {
    [Export ("drawAtPoint:withFont:")]
    CGSize DrawString (CGPoint point, UIFont font);
}

Bağımsız değişken listelerini bağlama Objective-C

Objective-C variadic bağımsız değişkenlerini destekler. Örneğin:

- (void) appendWorkers:(XWorker *) firstWorker, ...
  NS_REQUIRES_NIL_TERMINATION ;

C# dilinden bu yöntemi çağırmak için aşağıdaki gibi bir imza oluşturmak istersiniz:

[Export ("appendWorkers"), Internal]
void AppendWorkers (Worker firstWorker, IntPtr workersPtr)

Bu, yukarıdaki API'yi kullanıcılardan gizleyerek ancak kitaplığına kullanıma seçerek yöntemini dahili olarak bildirir. Ardından aşağıdaki gibi bir yöntem yazabilirsiniz:

public void AppendWorkers(params Worker[] workers)
{
    if (workers is null)
         throw new ArgumentNullException ("workers");

    var pNativeArr = Marshal.AllocHGlobal(workers.Length * IntPtr.Size);
    for (int i = 1; i < workers.Length; ++i)
        Marshal.WriteIntPtr (pNativeArr, (i - 1) * IntPtr.Size, workers[i].Handle);

    // Null termination
    Marshal.WriteIntPtr (pNativeArr, (workers.Length - 1) * IntPtr.Size, IntPtr.Zero);

    // the signature for this method has gone from (IntPtr, IntPtr) to (Worker, IntPtr)
    WorkerManager.AppendWorkers(workers[0], pNativeArr);
    Marshal.FreeHGlobal(pNativeArr);
}

Bağlama alanları

Bazen bir kitaplıkta bildirilen ortak alanlara erişmek istersiniz.

Bu alanlar genellikle başvurulması gereken dizeler veya tamsayı değerleri içerir. Bunlar genellikle belirli bir bildirimi temsil eden dize olarak ve sözlüklerde anahtarlar olarak kullanılır.

Bir alanı bağlamak için arabirim tanımı dosyanıza bir özellik ekleyin ve özelliğini özniteliğiyle [Field] süsleyin. Bu öznitelik bir parametre alır: aranan simgenin C adı. Örneğin:

[Field ("NSSomeEventNotification")]
NSString NSSomeEventNotification { get; }

'den NSObjecttüretilmeyen statik bir sınıftaki çeşitli alanları sarmalamak istiyorsanız, [Static] özniteliğini aşağıdaki gibi sınıfa ekleyin:

[Static]
interface LonelyClass {
    [Field ("NSSomeEventNotification")]
    NSString NSSomeEventNotification { get; }
}

Yukarıdaki, 'den NSObject türetilmeyen bir LonelyClass oluşturur ve olarak NSStringkullanıma sunulan öğesine NSSomeEventNotificationNSString bir bağlama içerir.

[Field] Özniteliği aşağıdaki veri türlerine uygulanabilir:

  • NSString başvurular (yalnızca salt okunur özellikler)
  • NSArray başvurular (yalnızca salt okunur özellikler)
  • 32 bit int (System.Int32)
  • 64 bit int (System.Int64)
  • 32 bit kayanlar (System.Single)
  • 64 bit kayanlar (System.Double)
  • System.Drawing.SizeF
  • CGSize

Yerel alan adına ek olarak, kitaplık adını geçirerek alanın bulunduğu kitaplık adını belirtebilirsiniz:

[Static]
interface LonelyClass {
    [Field ("SomeSharedLibrarySymbol", "SomeSharedLibrary")]
    NSString SomeSharedLibrarySymbol { get; }
}

Statik olarak bağlanıyorsanız bağlanılacak kitaplık yoktur, bu nedenle şu adı kullanmanız __Internal gerekir:

[Static]
interface LonelyClass {
    [Field ("MyFieldFromALibrary", "__Internal")]
    NSString MyFieldFromALibrary { get; }
}

Bağlama sabit listeleri

Farklı bir kaynak dosya kullanmadan (hem bağlamalarda hem de son projede derlenmesi gereken) API tanımları içinde kullanımı kolaylaştırmak için bağlama dosyalarınıza doğrudan ekleyebilirsiniz enum .

Örnek:

[Native] // needed for enums defined as NSInteger in ObjC
enum MyEnum {}

interface MyType {
    [Export ("initWithEnum:")]
    IntPtr Constructor (MyEnum value);
}

Sabitleri değiştirmek NSString için kendi sabit listelerinizi de oluşturabilirsiniz. Bu durumda oluşturucu , numaralandırma değerlerini ve NSString sabitlerini sizin için dönüştürme yöntemlerini otomatik olarak oluşturur.

Örnek:

enum NSRunLoopMode {

    [DefaultEnumValue]
    [Field ("NSDefaultRunLoopMode")]
    Default,

    [Field ("NSRunLoopCommonModes")]
    Common,

    [Field (null)]
    Other = 1000
}

interface MyType {
    [Export ("performForMode:")]
    void Perform (NSString mode);

    [Wrap ("Perform (mode.GetConstant ())")]
    void Perform (NSRunLoopMode mode);
}

Yukarıdaki örnekte bir [Internal] öznitelikle süslemeye void Perform (NSString mode); karar vekleyebilirsiniz. Bu, sabit tabanlı API'yi bağlama tüketicilerinizden gizler.

Ancak bu, daha iyi bir API alternatifi bir [Wrap] öznitelik kullandığından türün alt sınıflarını sınırlandırabilirsiniz. Bu oluşturulan yöntemler değildir virtual; örneğin, bunları geçersiz kılamazsınız; bu iyi bir seçim olabilir veya olmayabilir.

Alternatif olarak özgün, NSStringtabanlı tanımı olarak [Protected]işaretlemek gerekir. Bu, gerektiğinde alt sınıflamanın çalışmasına izin verir ve wrap'ed sürümü çalışmaya devam eder ve geçersiz kılma yöntemini çağırır.

, NSNumberve NSString öğesini daha iyi bir türe bağlama NSValue

[BindAs] özniteliği, NSValue ve NSString(sabit listelerinin) daha doğru C# türlerine bağlanmasına NSNumberizin verir. özniteliği, yerel API üzerinden daha iyi, daha doğru .NET API'sini oluşturmak için kullanılabilir.

ile yöntemleri (dönüş değerinde), parametreleri ve özellikleri [BindAs]süsleyebilirsiniz. Tek kısıtlama, üyenizin [Protocol] veya [Model] arabirimini seçin.

Örneğin:

[return: BindAs (typeof (bool?))]
[Export ("shouldDrawAt:")]
NSNumber ShouldDraw ([BindAs (typeof (CGRect))] NSValue rect);

Çıkış:

[Export ("shouldDrawAt:")]
bool? ShouldDraw (CGRect rect) { ... }

Dahili olarak ->NSNumber ve CGRect<-NSValue> dönüştürmelerini yapacağız.bool?<

[BindAs]ayrıca ve NSStringdizilerini NSNumberNSValue (sabit listeleri) destekler.

Örneğin:

[BindAs (typeof (CAScroll []))]
[Export ("supportedScrollModes")]
NSString [] SupportedScrollModes { get; set; }

Çıkış:

[Export ("supportedScrollModes")]
CAScroll [] SupportedScrollModes { get; set; }

CAScroll yedeklenmiş bir NSString sabit listesidir, doğru NSString değeri getirir ve tür dönüştürmeyi işleriz.

Desteklenen dönüştürme türlerini görmek için lütfen belgelere bakın [BindAs] .

Bağlama bildirimleri

Bildirimler, uygulamasına NSNotificationCenter.DefaultCenter gönderilen ve uygulamanın bir bölümünden diğerine ileti yayınlama mekanizması olarak kullanılan iletilerdir. Geliştiriciler genellikle NSNotificationCenter'nin AddObserver yöntemini kullanarak bildirimlere abone olur. Bir uygulama bildirim merkezine bir ileti gönderdiğinde, genellikle NSNotification.UserInfo sözlüğünde depolanan bir yük içerir. Kullanıcıların genellikle sözlükte hangi anahtarların kullanılabildiğini ve sözlükte depolanabilecek değerlerin türlerini belgelerde okuması gerektiğinden, bu sözlük zayıf bir şekilde yazılmıştır ve bu sözlükten bilgi almak hataya açıktır. Anahtarların varlığı bazen boole olarak da kullanılır.

Xamarin.iOS bağlama oluşturucu, geliştiricilerin bildirimleri bağlaması için destek sağlar. Bunu yapmak için [Notification] özniteliği ile etiketlenmiş bir özellikte [Field] özelliği (genel veya özel olabilir).

Bu öznitelik, yükü olmayan bildirimler için bağımsız değişkenler olmadan kullanılabilir veya API tanımında genellikle adı "EventArgs" ile biten başka bir arabirime başvuran bir System.Type belirtebilirsiniz. Oluşturucu, arabirimi alt sınıflara EventArgs dönüştürecek ve burada listelenen tüm özellikleri içerecektir. özniteliği EventArgs [Export] sınıfında, değeri getirmek için sözlüğü aramak için kullanılan anahtarın Objective-C adını listelemek için kullanılmalıdır.

Örneğin:

interface MyClass {
    [Notification]
    [Field ("MyClassDidStartNotification")]
    NSString DidStartNotification { get; }
}

Yukarıdaki kod aşağıdaki yöntemlerle iç içe geçmiş bir sınıf MyClass.Notifications oluşturur:

public class MyClass {
   [..]
   public Notifications {
      public static NSObject ObserveDidStart (EventHandler<NSNotificationEventArgs> handler)
   }
}

Kodunuzun kullanıcıları aşağıdaki gibi bir kod kullanarak NSDefaultCenter'ne gönderilen bildirimlere kolayca abone olabilir:

var token = MyClass.Notifications.ObserverDidStart ((notification) => {
    Console.WriteLine ("Observed the 'DidStart' event!");
});

döndürülen değeri ObserveDidStart , aşağıdaki gibi bildirimleri almayı kolayca durdurmak için kullanılabilir:

token.Dispose ();

İsterseniz NSNotification.DefaultCenter.RemoveObserver öğesini çağırabilir ve belirteci geçirebilirsiniz. Bildiriminiz parametreler içeriyorsa aşağıdaki gibi bir yardımcı EventArgs arabirim belirtmeniz gerekir:

interface MyClass {
    [Notification (typeof (MyScreenChangedEventArgs)]
    [Field ("MyClassScreenChangedNotification")]
    NSString ScreenChangedNotification { get; }
}

// The helper EventArgs declaration
interface MyScreenChangedEventArgs {
    [Export ("ScreenXKey")]
    nint ScreenX { get; set; }

    [Export ("ScreenYKey")]
    nint ScreenY { get; set; }

    [Export ("DidGoOffKey")]
    [ProbePresence]
    bool DidGoOff { get; }
}

Yukarıdaki, sırasıyla "ScreenXKey" ve "ScreenYKey" anahtarlarını kullanarak NSNotification.UserInfo sözlüğünden verileri getirecek ve ScreenY özelliklerine sahip ScreenX bir MyScreenChangedEventArgs sınıf oluşturur ve uygun dönüştürmeleri uygular. [ProbePresence] anahtarı değerini ayıklamaya çalışmak yerine içinde UserInfoayarlanmışsa oluşturucunun yoklaması için özniteliği kullanılır. Bu, anahtarın varlığının değer olduğu durumlar için kullanılır (genellikle boole değerleri için).

Bu, aşağıdaki gibi bir kod yazmanızı sağlar:

var token = MyClass.NotificationsObserveScreenChanged ((notification) => {
    Console.WriteLine ("The new screen dimensions are {0},{1}", notification.ScreenX, notification.ScreenY);
});

Bağlama kategorileri

Kategoriler, bir Objective-C sınıfta kullanılabilen yöntem ve özellik kümesini genişletmek için kullanılan bir mekanizmadır. Uygulamada, temel sınıfın işlevselliğini (örneğin NSObject, içinde belirli bir çerçeve bağlandığında) genişletmek için kullanılırlar (örneğin UIKit), yöntemlerini kullanılabilir hale getirir, ancak yalnızca yeni çerçeve bağlıysa. Bazı diğer durumlarda, bir sınıftaki özellikleri işlevlere göre düzenlemek için kullanılırlar. C# uzantı yöntemlerine benzerler. içinde bir kategori şöyle görünür Objective-C:

@interface UIView (MyUIViewExtension)
-(void) makeBackgroundRed;
@end

Yukarıdaki örnek, bir kitaplıkta bulunursa, öğesinin UIView örneklerini yöntemiyle makeBackgroundRedgenişletir.

Bunları bağlamak için bir arabirim tanımında özniteliğini [Category] kullanabilirsiniz. Kullanırken [Category] özniteliği, [BaseType] özniteliği, genişletilecek temel sınıfı belirtmek için kullanılmaktan, genişletilecek tür olacak şekilde değişir.

Aşağıda uzantıların UIView nasıl bağlı olduğu ve C# uzantı yöntemlerine nasıl dönüştürülmesi gösterilmektedir:

[BaseType (typeof (UIView))]
[Category]
interface MyUIViewExtension {
    [Export ("makeBackgroundRed")]
    void MakeBackgroundRed ();
}

Yukarıdaki, uzantı yöntemini içeren MakeBackgroundRed bir MyUIViewExtension sınıf oluşturur. Bu, artık herhangi bir UIView alt sınıfta "MakeBackgroundRed" öğesini çağırabileceğiniz ve size üzerinde Objective-Calabileceğiniz işlevselliği sağlayabileceğiniz anlamına gelir. Bazı durumlarda, kategoriler bir sistem sınıfını genişletmek için değil, yalnızca dekorasyon amacıyla işlevselliği düzenlemek için kullanılır. Böyle:

@interface SocialNetworking (Twitter)
- (void) postToTwitter:(Message *) message;
@end

@interface SocialNetworking (Facebook)
- (void) postToFacebook:(Message *) message andPicture: (UIImage*)
picture;
@end

Ancak şunu kullanabilirsiniz: [Category] özniteliği, bildirimlerin bu dekorasyon stili için de tümünü sınıf tanımına ekleyebilirsiniz. Bunların her ikisi de aynı başarıya ulaşır:

[BaseType (typeof (NSObject))]
interface SocialNetworking {
}

[Category]
[BaseType (typeof (SocialNetworking))]
interface Twitter {
    [Export ("postToTwitter:")]
    void PostToTwitter (Message message);
}

[Category]
[BaseType (typeof (SocialNetworking))]
interface Facebook {
    [Export ("postToFacebook:andPicture:")]
    void PostToFacebook (Message message, UIImage picture);
}

Bu durumlarda kategorileri birleştirmek daha kısadır:

[BaseType (typeof (NSObject))]
interface SocialNetworking {
    [Export ("postToTwitter:")]
    void PostToTwitter (Message message);

    [Export ("postToFacebook:andPicture:")]
    void PostToFacebook (Message message, UIImage picture);
}

Bağlama blokları

Bloklar, C# anonim yöntemlerinin işlevsel eşdeğerini öğesine getirmek için Apple tarafından sunulan yeni bir yapıdır Objective-C. Örneğin, NSSet sınıfı şimdi şu yöntemi kullanıma sunar:

- (void) enumerateObjectsUsingBlock:(void (^)(id obj, BOOL *stop) block

Yukarıdaki açıklama adlı bir bağımsız değişken blockalan adlı enumerateObjectsUsingBlock: bir yöntem bildirir. Bu blok, geçerli ortamı yakalama ("bu" işaretçisi, yerel değişkenlere ve parametrelere erişim) desteğine sahip olduğu C# anonim yöntemine benzer. yukarıdaki yöntemi, NSSet bloğu bir (bölüm) ve bir NSObject boole BOOL *stop(the id obj ) bölümüne yönelik bir işaretçi ile iki parametreyle çağırır.

Bu tür BIR API'yi btouch ile bağlamak için önce blok türü imzasını C# temsilcisi olarak bildirmeniz ve ardından bir API giriş noktasından buna başvurmanız gerekir, örneğin:

// This declares the callback signature for the block:
delegate void NSSetEnumerator (NSObject obj, ref bool stop)

// Later, inside your definition, do this:
[Export ("enumerateObjectUsingBlock:")]
void Enumerate (NSSetEnumerator enum)

Artık kodunuz işlevinizi C# dilinden çağırabilir:

var myset = new NSMutableSet ();
myset.Add (new NSString ("Foo"));

s.Enumerate (delegate (NSObject obj, ref bool stop){
    Console.WriteLine ("The first object is: {0} and stop is: {1}", obj, stop);
});

Dilerseniz lambdaları da kullanabilirsiniz:

var myset = new NSMutableSet ();
mySet.Add (new NSString ("Foo"));

s.Enumerate ((obj, stop) => {
    Console.WriteLine ("The first object is: {0} and stop is: {1}", obj, stop);
});

Zaman uyumsuz yöntemler

Bağlama oluşturucu, belirli bir yöntem sınıfını zaman uyumsuz yöntemlere (Görev veya Görev<T> döndüren yöntemler) dönüştürebilir.

Şunu kullanabilirsiniz: [Async] void döndüren ve son bağımsız değişkeni bir geri çağırma olan yöntemlerde özniteliği. Bunu bir yönteme uyguladığınızda, bağlama oluşturucu sonekiyle Asyncbu yöntemin bir sürümünü oluşturur. Geri çağırma hiçbir parametre almazsa, geri çağırma bir Taskparametre alırsa, sonuç bir Task<T>olur. Geri çağırma birden çok parametre alıyorsa, oluşturulan türün ResultType istenen adını belirterek tüm özellikleri barındıracak veya ResultTypeName değerini ayarlamanız gerekir.

Örnek:

[Export ("loadfile:completed:")]
[Async]
void LoadFile (string file, Action<string> completed);

Yukarıdaki kod hem LoadFile yöntemini hem de şunları oluşturur:

[Export ("loadfile:completed:")]
Task<string> LoadFileAsync (string file);

Zayıf NSDictionary parametreleri için güçlü türler oluşturma

API'nin birçok yerinde Objective-C , parametreler belirli anahtarlar ve değerler içeren zayıf türdeki NSDictionary API'ler olarak geçirilir, ancak bunlar hataya açıktır (geçersiz anahtarlar geçirebilir ve uyarı almazsınız; geçersiz değerler geçirebilir ve uyarı almazsınız) ve olası anahtar adlarını ve değerlerini aramaları için belgelere birden çok gidişe ihtiyaç duyduklarından kullanmak sinir bozucudur.

Çözüm, API'nin kesin olarak türü belirlenmiş sürümünü sağlayan ve arka planda çeşitli temel anahtarları ve değerleri eşleyen güçlü türe sahip bir sürüm sağlamaktır.

Örneğin, API bir NSDictionary değerini kabul ettiyse ve birim değeri 0,0 ile 1,0 XyzCaptionKey arasında olan ve dize alan bir değeri alan anahtarın XyzVolumeKey alınması NSNumber olarak belgelenmişseObjective-C, kullanıcılarınızın şuna benzer güzel bir API'ye sahip olmasını istersiniz:

public class  XyzOptions {
    public nfloat? Volume { get; set; }
    public string Caption { get; set; }
}

Volume özelliği null atanabilir kayan değer olarak tanımlanır, kuralı Objective-C bu sözlüklerin değere sahip olmasını gerektirmediğinden değerin ayarlanmayabileceği senaryolar vardır.

Bunu yapmak için birkaç şey yapmanız gerekir:

  • DictionaryContainer'ı alt sınıflandıran ve her özellik için çeşitli alıcıları ve ayarlayıcıları sağlayan kesin türe sahip bir sınıf oluşturun.
  • Yeni güçlü türe sahip sürümü almak için alan NSDictionary yöntemler için aşırı yüklemeleri bildirin.

Kesin olarak türü belirlenmiş sınıfı el ile oluşturabilir veya sizin için işi yapmak için oluşturucuyu kullanabilirsiniz. Öncelikle neler olduğunu ve ardından otomatik yaklaşımı anlamanız için bunu el ile nasıl yapacağınızı keşfedeceğiz.

Bunun için bir destekleyici dosya oluşturmanız gerekir, bu dosya sözleşme API'nize gitmez. XyzOptions sınıfınızı oluşturmak için yazmanız gereken şey budur:

public class XyzOptions : DictionaryContainer {
# if !COREBUILD
    public XyzOptions () : base (new NSMutableDictionary ()) {}
    public XyzOptions (NSDictionary dictionary) : base (dictionary){}

    public nfloat? Volume {
       get { return GetFloatValue (XyzOptionsKeys.VolumeKey); }
       set { SetNumberValue (XyzOptionsKeys.VolumeKey, value); }
    }
    public string Caption {
       get { return GetStringValue (XyzOptionsKeys.CaptionKey); }
       set { SetStringValue (XyzOptionsKeys.CaptionKey, value); }
    }
# endif
}

Daha sonra, alt düzey API'nin üzerinde üst düzey API'yi ortaya çıkaran bir sarmalayıcı yöntemi sağlamanız gerekir.

[BaseType (typeof (NSObject))]
interface XyzPanel {
    [Export ("playback:withOptions:")]
    void Playback (string fileName, [NullAllowed] NSDictionary options);

    [Wrap ("Playback (fileName, options?.Dictionary")]
    void Playback (string fileName, XyzOptions options);
}

API'nizin üzerine yazılması gerekmiyorsa, NSDictionary tabanlı API'yi kullanarak güvenli bir şekilde gizleyebilirsiniz [Internal] özniteliği.

Gördüğünüz gibi [Wrap] özniteliğini kullanarak yeni bir API giriş noktasını ortaya çıkarıyoruz ve bunu güçlü türe sahip XyzOptions sınıfımızı kullanarak ortaya çıkarıyoruz. Sarmalayıcı yöntemi null değerinin geçirilmesine de izin verir.

Şimdi, bahsetmediğimiz bir şey değerlerin XyzOptionsKeys nereden geldiğidir. Normalde bir API'nin gibi XyzOptionsKeysstatik bir sınıfta ortaya çıkaracağı anahtarları şu şekilde gruplandırırsınız:

[Static]
class XyzOptionKeys {
    [Field ("kXyzVolumeKey")]
    NSString VolumeKey { get; }

    [Field ("kXyzCaptionKey")]
    NSString CaptionKey { get; }
}

Bu güçlü türdeki sözlükleri oluşturmak için otomatik desteğe göz atalım. Bu, çok sayıda ortak değeri önler ve sözlüğü dış dosya kullanmak yerine doğrudan API sözleşmenizde tanımlayabilirsiniz.

Kesin olarak türlenmiş bir sözlük oluşturmak için API'nize bir arabirim ekleyin ve Bunu StrongDictionary özniteliğiyle süsleyin. Bu, oluşturucuya, arabiriminizle aynı ada sahip bir sınıf oluşturması gerektiğini ve bu sınıftan DictionaryContainer türetilecek ve bunun için güçlü türetilmiş erişimcileri sağlayacağını söyler.

[StrongDictionary] özniteliği, sözlük anahtarlarınızı içeren statik sınıfın adı olan bir parametre alır. Ardından arabirimin her özelliği kesin olarak türlenmiş bir erişimciye dönüşür. Varsayılan olarak kod, erişimciyi oluşturmak için statik sınıfta "Key" soneki ile özelliğinin adını kullanır.

Bu, kesin olarak belirlenmiş erişimcinizi oluşturmanın artık bir dış dosya gerektirmediği, her özellik için el ile alıcı ve ayarlayıcı oluşturmak zorunda kalmadığı veya anahtarları kendiniz aramanız gerektirdiği anlamına gelir.

Bağlamanızın tamamı şöyle görünür:

[Static]
class XyzOptionKeys {
    [Field ("kXyzVolumeKey")]
    NSString VolumeKey { get; }

    [Field ("kXyzCaptionKey")]
    NSString CaptionKey { get; }
}
[StrongDictionary ("XyzOptionKeys")]
interface XyzOptions {
    nfloat Volume { get; set; }
    string Caption { get; set; }
}

[BaseType (typeof (NSObject))]
interface XyzPanel {
    [Export ("playback:withOptions:")]
    void Playback (string fileName, [NullAllowed] NSDictionary options);

    [Wrap ("Playback (fileName, options?.Dictionary")]
    void Playback (string fileName, XyzOptions options);
}

Üyelerinizde XyzOption farklı bir alana başvurmanız gerekirse (sonek Keyile özelliğin adı değildir), özelliği bir ile süsleyebilirsiniz [Export] özniteliğini kullanın.

Tür eşlemeleri

Bu bölümde türlerin C# türlerine nasıl Objective-C eşlendiği açıklanmaktadır.

Basit türler

Aşağıdaki tabloda, ve CocoaTouch dünyasındaki Objective-C türleri Xamarin.iOS dünyasına nasıl eşlemeniz gerektiği gösterilmektedir:

Objective-C tür adı Xamarin.iOS Birleşik API türü
BOOL, GLboolean bool
NSInteger nint
NSUInteger nuint
CFTimeInterval / NSTimeInterval double
NSString (NSString bağlama hakkında daha fazla bilgi) string
char * string (ayrıca bkz: [PlainString])
CGRect CGRect
CGPoint CGPoint
CGSize CGSize
CGFloat, GLfloat nfloat
CoreFoundation türleri (CF*) CoreFoundation.CF*
GLint nint
GLfloat nfloat
Temel türleri (NS*) Foundation.NS*
id Foundation.NSObject
NSGlyph nint
NSSize CGSize
NSTextAlignment UITextAlignment
SEL ObjCRuntime.Selector
dispatch_queue_t CoreFoundation.DispatchQueue
CFTimeInterval double
CFIndex nint
NSGlyph nuint

Diziler

Xamarin.iOS çalışma zamanı, C# dizilerini NSArrays öğesine dönüştürmeyi ve dönüştürmeyi geri yapmayı otomatik olarak üstlenir; bu nedenle örneğin, bir NSArrayUIViewsöğesini döndüren sanal Objective-C yöntem:

// Get the peer views - untyped
- (NSArray *)getPeerViews ();

// Set the views for this container
- (void) setViews:(NSArray *) views

Şu şekilde bağlıdır:

[Export ("getPeerViews")]
UIView [] GetPeerViews ();

[Export ("setViews:")]
void SetViews (UIView [] views);

Amaç, IDE'nin kullanıcıyı tahmin etmeye zorlamadan gerçek türle doğru kod tamamlama sağlamasına olanak tanıyacağı için kesin olarak belirlenmiş bir C# dizisi kullanmak veya dizide yer alan nesnelerin gerçek türünü bulmak için belgeleri aramaktır.

Dizide yer alan gerçek en türetilmiş türü izleyemediyseniz, dönüş değeri olarak kullanabilirsiniz NSObject [] .

Seçiciler

Seçiciler API'de Objective-C özel tür SELolarak görünür. Seçiciyi bağlarken, türünü ile ObjCRuntime.Selectoreşlersiniz. Seçiciler genellikle hem bir nesne, hem hedef nesne hem de hedef nesnede çağrılacak bir seçici ile bir API'de kullanıma sunulur. Bunların her ikisini de sağlamak temel olarak C# temsilcisine karşılık gelir: hem çağrılacak yöntemi hem de yöntemini çağırmak için nesneyi kapsülleyen bir şey.

Bağlama şöyle görünür:

interface Button {
   [Export ("setTarget:selector:")]
   void SetTarget (NSObject target, Selector sel);
}

Yöntem genellikle bir uygulamada şu şekilde kullanılır:

class DialogPrint : UIViewController {
    void HookPrintButton (Button b)
    {
        b.SetTarget (this, new Selector ("print"));
    }

    [Export ("print")]
    void ThePrintMethod ()
    {
       // This does the printing
    }
}

Bağlamayı C# geliştiricilerine daha iyi hale getirmek için genellikle parametresini alan ve C# temsilcilerinin ve lambdaların yerine Target+Selectorkullanılmasına olanak tanıyan bir NSAction yöntem sağlarsınız. Bunu yapmak için genellikle yöntemi bir ile işaretleyerek gizleyebilirsiniz SetTarget[Internal] özniteliğini ve ardından aşağıdaki gibi yeni bir yardımcı yöntemi kullanıma sunarsınız:

// API.cs
interface Button {
   [Export ("setTarget:selector:"), Internal]
   void SetTarget (NSObject target, Selector sel);
}

// Extensions.cs
public partial class Button {
     public void SetTarget (NSAction callback)
     {
         SetTarget (new NSActionDispatcher (callback), NSActionDispatcher.Selector);
     }
}

Bu nedenle kullanıcı kodunuz şu şekilde yazılabilir:

class DialogPrint : UIViewController {
    void HookPrintButton (Button b)
    {
        // First Style
        b.SetTarget (ThePrintMethod);

        // Lambda style
        b.SetTarget (() => {  /* print here */ });
    }

    void ThePrintMethod ()
    {
       // This does the printing
    }
}

Dizeler

alan bir yöntemi NSStringbağlarken, bunu hem dönüş türlerinde hem de parametrelerde bir C# dize türüyle değiştirebilirsiniz.

Doğrudan kullanmak NSString isteyebileceğiniz tek durum, dizenin belirteç olarak kullanılmasıdır. ve NSStringdizeleri hakkında daha fazla bilgi için NSString üzerinde API Tasarımı belgesini okuyun.

Bazı nadir durumlarda API, dize () yerine C benzeri dizeyi Objective-C (char *NSString *) kullanıma sunar. Bu gibi durumlarda parametresine şu şekilde açıklama ekleyebilirsiniz: [PlainString] özniteliği.

out/ref parametreleri

Bazı API'ler parametrelerinde değer döndürür veya parametreleri başvuruya göre geçirir.

İmza genellikle şöyle görünür:

- (void) someting:(int) foo withError:(NSError **) retError
- (void) someString:(NSObject **)byref

İlk örnekte hata kodları döndürmek için ortak Objective-C bir deyim gösterilir, işaretçi işaretçisi NSError geçirilir ve döndürülen değer ayarlanır. İkinci yöntem, bir yöntemin Objective-C bir nesneyi nasıl alıp içeriğini değiştirebileceğini gösterir. Bu, saf bir çıkış değeri yerine başvuruya göre geçiş olacaktır.

Bağlamanız şöyle görünür:

[Export ("something:withError:")]
void Something (nint foo, out NSError error);
[Export ("someString:")]
void SomeString (ref NSObject byref);

Bellek yönetimi öznitelikleri

özniteliğini [Export] kullandığınızda ve çağrılan yöntem tarafından tutulacak verileri geçirirken, bağımsız değişken semantiğini ikinci parametre olarak geçirerek belirtebilirsiniz, örneğin:

[Export ("method", ArgumentSemantic.Retain)]

Yukarıdaki değeri "Koru" semantiğine sahip olarak işaretlemektedir. Kullanılabilir semantikler şunlardır:

  • Ata
  • Kopyala
  • Tut

Stil yönergeleri

[internal] kullanma

Şunu kullanabilirsiniz: [Internal] özniteliğini kullanarak bir yöntemi genel API'den gizleyin. Kullanıma sunulan API'nin çok düşük düzeyde olduğu ve bu yöntemi temel alan ayrı bir dosyada üst düzey bir uygulama sağlamak istediğiniz durumlarda bunu yapmak isteyebilirsiniz.

Bağlama oluşturucusunda sınırlamalarla karşı karşıya olduğunuzda da bunu kullanabilirsiniz. Örneğin bazı gelişmiş senaryolar, bağlı olmayan ve kendi yönteminizle bağlamak istediğiniz türleri kullanıma açabilir ve bu türleri kendi yönteminizle sarmalamak isteyebilirsiniz.

Olay işleyicileri ve geri çağırmalar

Objective-C sınıflar genellikle bir temsilci sınıfına (Objective-C temsilci) ileti göndererek bildirimleri veya istek bilgilerini yayınlar.

Bu model, Xamarin.iOS tarafından tam olarak desteklenip ortaya çıkarılırken bazen hantal olabilir. Xamarin.iOS, C# olay desenini ve bu durumlarda kullanılabilecek bir method-callback sistemini sınıfta kullanıma sunar. Bu, aşağıdaki gibi kodun çalışmasına izin verir:

button.Clicked += delegate {
    Console.WriteLine ("I was clicked");
};

Bağlama oluşturucu, deseni C# desenine eşlemek Objective-C için gereken yazma miktarını azaltabilir.

Xamarin.iOS 1.4'den başlayarak, oluşturucuya belirli Objective-C temsilciler için bağlamalar oluşturmasını ve temsilciyi konak türünde C# olayları ve özellikleri olarak kullanıma sunmasını bildirmek de mümkün olacaktır.

Bu işlemde iki sınıf vardır: konak sınıfı, şu anda olayları yayan ve bunları Delegate veya WeakDelegate ve gerçek temsilci sınıfına gönderen sınıftır.

Aşağıdaki kurulumu göz önünde bulundurarak:

[BaseType (typeof (NSObject))]
interface MyClass {
    [Export ("delegate", ArgumentSemantic.Assign)][NullAllowed]
    NSObject WeakDelegate { get; set; }

    [Wrap ("WeakDelegate")][NullAllowed]
    MyClassDelegate Delegate { get; set; }
}

[BaseType (typeof (NSObject))]
interface MyClassDelegate {
    [Export ("loaded:bytes:")]
    void Loaded (MyClass sender, int bytes);
}

Sınıfı sarmalamanız için:

  • Konak sınıfınızda [BaseType]
    temsilcisi olarak davranan türü ve kullanıma sunduğun C# adını belirtir. Yukarıdaki örneğimizde bunlar sırasıyla ve WeakDelegate şeklindedirtypeof (MyClassDelegate).
  • Temsilci sınıfınızda, ikiden fazla parametresi olan her yöntemde, otomatik olarak oluşturulan EventArgs sınıfı için kullanmak istediğiniz türü belirtmeniz gerekir.

Bağlama oluşturucu yalnızca tek bir olay hedefini sarmalamayla sınırlı değildir, bazı Objective-C sınıfların iletileri birden fazla temsilciye yayması mümkündür, bu nedenle bu kurulumu desteklemek için diziler sağlamanız gerekir. Çoğu kurulum buna ihtiyaç duymaz, ancak oluşturucu bu durumları desteklemeye hazırdır.

Sonuçta elde edilen kod şöyle olacaktır:

[BaseType (typeof (NSObject),
    Delegates=new string [] {"WeakDelegate"},
    Events=new Type [] { typeof (MyClassDelegate) })]
interface MyClass {
        [Export ("delegate", ArgumentSemantic.Assign)][NullAllowed]
        NSObject WeakDelegate { get; set; }

        [Wrap ("WeakDelegate")][NullAllowed]
        MyClassDelegate Delegate { get; set; }
}

[BaseType (typeof (NSObject))]
interface MyClassDelegate {
        [Export ("loaded:bytes:"), EventArgs ("MyClassLoaded")]
        void Loaded (MyClass sender, int bytes);
}

EventArgs, oluşturulacak sınıfın EventArgs adını belirtmek için kullanılır. İmza başına bir tane kullanmalısınız (bu örnekte, EventArgs nint türünde bir With özellik içerecektir).

Yukarıdaki tanımlarla, oluşturucu oluşturulan MyClass'ta aşağıdaki olayı üretir:

public MyClassLoadedEventArgs : EventArgs {
        public MyClassLoadedEventArgs (nint bytes);
        public nint Bytes { get; set; }
}

public event EventHandler<MyClassLoadedEventArgs> Loaded {
        add; remove;
}

Bu nedenle kodu şu şekilde kullanabilirsiniz:

MyClass c = new MyClass ();
c.Loaded += delegate (sender, args){
        Console.WriteLine ("Loaded event with {0} bytes", args.Bytes);
};

Geri çağırmalar aynı olay çağrıları gibidir. Fark, birden çok olası aboneye sahip olmak yerine (örneğin, birden çok yöntem bir Clicked olaya veya DownloadFinished olaya bağlanabilir) geri çağırmaların yalnızca tek bir aboneye sahip olmasıdır.

İşlem aynıdır, tek fark, oluşturulacak sınıfın adını ortaya çıkarmak yerine EventArgs'in EventArgs sonuçta elde edilen C# temsilci adını adlandırmak için kullanılmasıdır.

Temsilci sınıfındaki yöntem bir değer döndürürse, bağlama oluşturucu bunu bir olay yerine üst sınıftaki bir temsilci yöntemiyle eşler. Bu durumlarda, kullanıcı temsilciye bağlanmazsa yöntemi tarafından döndürülmesi gereken varsayılan değeri sağlamanız gerekir. Bunu şu şekilde yaparsınız: [DefaultValue] veya [DefaultValueFromArgument] özniteliklerini seçin.

[DefaultValue] bir dönüş değerini sabit kodlarken [DefaultValueFromArgument] , hangi giriş bağımsız değişkeninin döndürüleceğini belirtmek için kullanılır.

Numaralandırmalar ve temel türler

Ayrıca, btouch arabirim tanım sistemi tarafından doğrudan desteklenmeyen sabit listelerine veya temel türlere de başvurabilirsiniz. Bunu yapmak için, numaralandırmalarınızı ve çekirdek türlerinizi ayrı bir dosyaya yerleştirin ve bunu, btouch'a sağladığınız ek dosyalardan birinin parçası olarak ekleyin.

Bağımlılıkları bağlama

Uygulamanızın parçası olmayan API'leri bağlıyorsanız, yürütülebilir dosyanızın bu kitaplıklara bağlı olduğundan emin olmanız gerekir.

Xamarin.iOS'a kitaplıklarınızı nasıl bağlayabileceğinizi bildirmeniz gerekir. Bu, "-gcc_flags" seçeneğini kullanarak yeni kitaplıklarla nasıl bağlantı oluşturulacağını belirten ek derleme bağımsız değişkenleriyle komutu çağırmak mtouch için derleme yapılandırmanızı değiştirerek ve ardından programınız için gerekli olan tüm ek kitaplıkları içeren tırnak içinde bir dizeyle yapılabilir. Böyle:

-gcc_flags "-L$(MSBuildProjectDirectory) -lMylibrary -force_load -lSystemLibrary -framework CFNetwork -ObjC"

Yukarıdaki örnek, libSystemLibrary.dylib ve çerçeve kitaplığını CFNetwork son yürütülebilir dosyanıza bağlarlibMyLibrary.a.

Alternatif olarak, sözleşme dosyalarınıza (gibiAssemblyInfo.cs) ekleyebileceğiniz derleme düzeyinden [LinkWithAttribute]de yararlanabilirsiniz. kullandığınızda [LinkWithAttribute], bağlamanızı yaparken yerel kitaplığınızın kullanılabilir olması gerekir, bu da yerel kitaplığı uygulamanızla birlikte ekler. Örneğin:

// Specify only the library name as a constructor argument and specify everything else with properties:
[assembly: LinkWith ("libMyLibrary.a", LinkTarget = LinkTarget.ArmV6 | LinkTarget.ArmV7 | LinkTarget.Simulator, ForceLoad = true, IsCxx = true)]

// Or you can specify library name *and* link target as constructor arguments:
[assembly: LinkWith ("libMyLibrary.a", LinkTarget.ArmV6 | LinkTarget.ArmV7 | LinkTarget.Simulator, ForceLoad = true, IsCxx = true)]

Neden komutuna ihtiyacınız -force_load olduğunu merak ediyor olabilirsiniz ve bunun nedeni - ObjC bayrağı kodu içinde derlese de, Xamarin.iOS için çalışma zamanında ihtiyacınız olan kategorileri desteklemek için gereken meta verileri korumamasıdır (bağlayıcı/derleyici ölü kod yok etme bunu kaldırır).

Yardımlı başvurular

Eylem sayfaları ve uyarı kutuları gibi bazı geçici nesneler geliştiriciler için takip etmek için zahmetlidir ve bağlama oluşturucu burada biraz yardımcı olabilir.

Örneğin, bir ileti gösteren ve sonra bir olay oluşturan bir Done sınıfınız varsa, bunu işlemenin geleneksel yolu şu olacaktır:

class Demo {
    MessageBox box;

    void ShowError (string msg)
    {
        box = new MessageBox (msg);
        box.Done += { box = null; ... };
    }
}

Yukarıdaki senaryoda geliştiricinin nesneye başvuruyu kendi başına tutması ve kutu başvuruyu kendi başına sızdırması veya etkin bir şekilde temizlemesi gerekir. Kod bağlanırken, oluşturucu sizin için başvurunun izlenmesini destekler ve özel bir yöntem çağrıldığında bunu temizler; yukarıdaki kod şu hale gelir:

class Demo {
    void ShowError (string msg)
    {
        var box = new MessageBox (msg);
        box.Done += { ... };
    }
}

Değişkenin bir örnekte tutulmasının artık gerekli olmadığına, yerel bir değişkenle çalıştığına ve nesne öldüğünde başvurunun temizlenmesinin gerekli olmadığına dikkat edin.

Bundan yararlanmak için sınıfınızın bildiriminde [BaseType] ayarlanmış bir Events özelliğine ve ayrıca KeepUntilRef nesnesi çalışmasını tamamladığında çağrılan yöntemin adına ayarlanmış değişkenine sahip olması gerekir, örneğin:

[BaseType (typeof (NSObject), KeepUntilRef="Dismiss"), Delegates=new string [] { "WeakDelegate" }, Events=new Type [] { typeof (SomeDelegate) }) ]
class Demo {
    [Export ("show")]
    void Show (string message);
}

Protokolleri devralma

Xamarin.iOS v3.2 itibarıyla özelliğiyle [Model] işaretlenmiş protokollerden devralmayı destekliyoruz. Bu, protokolün protokolden devraldığı ve öğesinden MKAnnotation devralan NSObjectbir dizi sınıf tarafından benimsendiği durumlarda MKOverlay olduğu gibi bazı API desenlerinde MapKit kullanışlıdır.

Geçmişte protokolün her uygulamaya kopyalanması gerekiyordu, ancak bu gibi durumlarda sınıfın MKShape protokolden devralınabilmesini MKOverlay sağlayabiliriz ve gerekli tüm yöntemleri otomatik olarak oluşturur.