Desain API Xamarin.iOS

Selain Pustaka Kelas Dasar inti yang merupakan bagian dari Mono, Xamarin.iOS dikirim dengan pengikatan untuk berbagai API iOS untuk memungkinkan pengembang membuat aplikasi iOS asli dengan Mono.

Inti dari Xamarin.iOS, ada mesin interop yang menjenjang dunia C# dengan Objective-C dunia, dan pengikatan untuk API berbasis C iOS seperti CoreGraphics dan OpenGL ES.

Runtime tingkat rendah untuk berkomunikasi dengan Objective-C kode ada di MonoTouch.ObjCRuntime. Selain itu, pengikatan untuk Foundation, CoreFoundation, dan UIKit disediakan.

Prinsip Desain

Bagian ini merinci beberapa prinsip desain kami untuk pengikatan Xamarin.iOS (juga berlaku untuk Xamarin.Mac, pengikatan Mono untuk Objective-C di macOS):

  • Ikuti Panduan Desain Kerangka Kerja

  • Izinkan pengembang untuk subkelas Objective-C kelas:

    • Berasal dari kelas yang ada
    • Panggil konstruktor dasar untuk menautkan
    • Metode penimpaan harus dilakukan dengan sistem penimpaan C#
    • Subkelas harus berfungsi dengan konstruksi standar C#
  • Jangan mengekspos pengembang ke Objective-C pemilih

  • Menyediakan mekanisme untuk memanggil pustaka arbitrer Objective-C

  • Membuat tugas umum Objective-C menjadi mudah dan tugas keras Objective-C dimungkinkan

  • Mengekspos Objective-C properti sebagai properti C#

  • Mengekspos API yang sangat ditik:

    • Tingkatkan keamanan jenis
    • Meminimalkan kesalahan runtime
    • Mendapatkan IDE IntelliSense pada jenis pengembalian
    • Memungkinkan dokumentasi popup IDE
  • Dorong eksplorasi in-IDE API:

    • Misalnya, alih-alih mengekspos array yang ditik lemah, seperti ini:

      NSArray *getViews
      

      Mengekspos jenis yang kuat, seperti ini:

      NSView [] Views { get; set; }
      

      Menggunakan jenis yang kuat memberi Visual Studio untuk Mac kemampuan untuk melakukan pelengkapan otomatis saat menelusuri API, membuat semua System.Array operasi tersedia pada nilai yang dikembalikan, dan memungkinkan nilai pengembalian untuk berpartisipasi dalam LINQ.

  • Jenis C# asli:

    • NSString menjadi string

    • Ubah int dan uint parameter yang seharusnya enumerasi menjadi enumerasi C# dan enumerasi C# dengan [Flags] atribut

    • Alih-alih objek jenis netral NSArray , ekspos array sebagai array yang di ketik dengan kuat.

    • Untuk peristiwa dan pemberitahuan, beri pengguna pilihan antara:

      • Versi yang sangat ditik secara default
      • Versi yang ditik lemah untuk kasus penggunaan tingkat lanjut
  • Objective-C Mendukung pola delegasi:

    • Sistem peristiwa C#
    • Mengekspos delegasi C# (lambda, metode anonim, dan System.Delegate) ke Objective-C API sebagai blok

Rakitan

Xamarin.iOS mencakup banyak rakitan yang merupakan Profil Xamarin.iOS. Halaman Rakitan memiliki informasi lebih lanjut.

Namespace Utama

ObjCRuntime

Namespace ObjCRuntime memungkinkan pengembang untuk menjembatani dunia antara C# dan Objective-C. Ini adalah pengikatan baru, yang dirancang khusus untuk iOS, berdasarkan pengalaman dari Cocoa# dan Gtk#.

Fondasi

Namespace foundation menyediakan jenis data dasar yang dirancang untuk beroperasi dengan Objective-C kerangka kerja Foundation yang merupakan bagian dari iOS dan merupakan dasar untuk pemrograman berorientasi objek di Objective-C.

Xamarin.iOS mencerminkan dalam C# hierarki kelas dari Objective-C. Misalnya, Objective-C NSObject kelas dasar dapat digunakan dari C# melalui Foundation.NSObject.

Meskipun namespace foundation menyediakan pengikatan untuk jenis Foundation yang mendasar Objective-C , dalam beberapa kasus kami telah memetakan jenis yang mendasar ke jenis .NET. Contohnya:

  • Alih-alih berurusan dengan NSString dan NSArray, runtime mengeksposnya sebagai stringC# dan array yangsangat diketik di seluruh API.

  • Berbagai API pembantu diekspos di sini untuk memungkinkan pengembang mengikat API pihak Objective-C ketiga, API iOS lainnya, atau API yang saat ini tidak terikat oleh Xamarin.iOS.

Untuk informasi selengkapnya tentang mengikat API, lihat bagian Generator Pengikatan Xamarin.iOS.

NSObject

Jenis NSObject adalah fondasi untuk semua pengikatan Objective-C . Jenis Xamarin.iOS mencerminkan dua kelas jenis dari API CocoaTouch iOS: jenis C (biasanya disebut sebagai jenis CoreFoundation) dan Objective-C jenis (yang semuanya berasal dari kelas NSObject).

Untuk setiap jenis yang mencerminkan jenis yang tidak dikelola, dimungkinkan untuk mendapatkan objek asli melalui properti Handel .

Meskipun Mono akan menyediakan pengumpulan sampah untuk semua objek Anda, Foundation.NSObject mengimplementasikan antarmuka System.IDisposable . Anda dapat secara eksplisit merilis sumber daya NSObject tertentu tanpa harus menunggu Pengumpul Sampah masuk. Melepaskan sumber daya secara eksplisit penting ketika Anda menggunakan NSObjects berat, misalnya, UIImages yang mungkin menyimpan pointer ke blok data yang besar.

Jika jenis Anda perlu melakukan finalisasi deterministik, ambil alih metode NSObject.Dispose(bool) Parameter untuk Dispose adalah "bool disposing", dan jika diatur ke true itu berarti bahwa metode Buang Anda sedang dipanggil karena pengguna secara eksplisit disebut Buang () pada objek. Nilai palsu berarti metode Buang (pembuangan bool) Anda sedang dipanggil dari finalizer pada utas finalizer.

Kategori

Dimulai dengan Xamarin.iOS 8.10 dimungkinkan untuk membuat Objective-C kategori dari C#.

Ini dilakukan menggunakan Category atribut , menentukan jenis untuk diperluas sebagai argumen ke atribut . Contoh berikut akan misalnya memperluas NSString.

[Category (typeof (NSString))]

Setiap metode kategori menggunakan mekanisme normal untuk mengekspor metode untuk Objective-C menggunakan Export atribut :

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

Semua metode ekstensi terkelola harus statis, tetapi dimungkinkan untuk membuat Objective-C metode instans menggunakan sintaks standar untuk metode ekstensi di C#:

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

dan argumen pertama ke metode ekstensi akan menjadi instans tempat metode dipanggil.

Contoh lengkap:

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

Contoh ini akan menambahkan metode instans toUpper asli ke kelas NSString, yang dapat dipanggil dari Objective-C.

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

Salah satu skenario di mana ini berguna adalah menambahkan metode ke seluruh set kelas di basis kode Anda, misalnya, ini akan membuat semua UIViewController instans melaporkan bahwa mereka dapat memutar:

[Category (typeof (UINavigationController))]
class Rotation_IOS6 {
      [Export ("shouldAutorotate:")]
      static bool ShouldAutoRotate (this UINavigationController self)
      {
          return true;
      }
}
PreserveAttribute

PreserveAttribute adalah atribut kustom yang digunakan untuk memberi tahu mtouch – alat penyebaran Xamarin.iOS – untuk mempertahankan jenis, atau anggota jenis, selama fase ketika aplikasi diproses untuk mengurangi ukurannya.

Setiap anggota yang tidak secara statis ditautkan oleh aplikasi akan dihapus. Oleh karena itu, atribut ini digunakan untuk menandai anggota yang tidak dirujuk secara statis, tetapi itu masih diperlukan oleh aplikasi Anda.

Misalnya, jika Anda membuat instans jenis secara dinamis, Anda mungkin ingin mempertahankan konstruktor default jenis Anda. Jika Anda menggunakan serialisasi XML, Anda mungkin ingin mempertahankan properti jenis Anda.

Anda dapat menerapkan atribut ini pada setiap anggota jenis, atau pada jenis itu sendiri. Jika Anda ingin mempertahankan seluruh jenis, Anda dapat menggunakan sintaks [Pertahankan (AllMembers = true)] pada jenis .

UIKit

Namespace layanan UIKit berisi pemetaan satu-ke-satu ke semua komponen UI yang membentuk CocoaTouch dalam bentuk kelas C#. API telah dimodifikasi untuk mengikuti konvensi yang digunakan dalam bahasa C#.

Delegasi C# disediakan untuk operasi umum. Untuk informasi selengkapnya, lihat bagian delegasi .

OpenGLES

Untuk OpenGLES, kami mendistribusikan versi API OpenTK yang dimodifikasi, pengikatan berorientasi objek ke OpenGL yang telah dimodifikasi untuk menggunakan jenis dan struktur data CoreGraphics, dan hanya mengekspos fungsionalitas yang tersedia di iOS.

Fungsionalitas OpenGLES 1.1 tersedia melalui jenis ES11.GL.

Fungsionalitas OpenGLES 2.0 tersedia melalui jenis ES20.GL.

Fungsionalitas OpenGLES 3.0 tersedia melalui jenis ES30.GL.

Desain Pengikatan

Xamarin.iOS bukan hanya pengikatan ke platform yang mendasar Objective-C . Ini memperluas sistem jenis .NET dan mengirimkan sistem untuk memadukan C# dan Objective-C.

Sama seperti P/Invoke adalah alat yang berguna untuk memanggil pustaka asli di Windows dan Linux, atau karena dukungan IJW dapat digunakan untuk interop COM pada Windows, Xamarin.iOS memperluas runtime untuk mendukung pengikatan objek C# ke Objective-C objek.

Diskusi di beberapa bagian berikutnya tidak diperlukan bagi pengguna yang membuat aplikasi Xamarin.iOS, tetapi akan membantu pengembang memahami bagaimana hal-hal dilakukan dan akan membantu mereka saat membuat aplikasi yang lebih rumit.

Jenis

Di mana masuk akal, jenis C# diekspos alih-alih jenis Foundation tingkat rendah, ke alam semesta C#. Ini berarti bahwa API menggunakan jenis "string" C# alih-alih NSString dan menggunakan array C# yang sangat diketik alih-alih mengekspos NSArray.

Secara umum, dalam desain Xamarin.iOS dan Xamarin.Mac, objek yang mendasar NSArray tidak terekspos. Sebagai gantinya, runtime secara otomatis mengonversi NSArrays ke array yang ditik dengan kuat dari beberapa NSObject kelas. Jadi, Xamarin.iOS tidak mengekspos metode yang diketik lemah seperti GetViews untuk mengembalikan NSArray:

NSArray GetViews ();

Sebagai gantinya, pengikatan mengekspos nilai pengembalian yang sangat ditik, seperti ini:

UIView [] GetViews ();

Ada beberapa metode yang diekspos dalam NSArray, untuk kasus sudut di mana Anda mungkin ingin menggunakan secara NSArray langsung, tetapi penggunaannya tidak dianjurkan dalam pengikatan API.

Selain itu, di API Klasik alih-alih mengekspos CGRect, CGPoint, dan CGSize dari API CoreGraphics, kami menggantinya dengan System.Drawing implementasi RectangleF, , PointFdan SizeF karena itu akan membantu pengembang mempertahankan kode OpenGL yang ada yang menggunakan OpenTK. Saat menggunakan API Terpadu 64-bit baru, API CoreGraphics harus digunakan.

Warisan

Desain Xamarin.iOS API memungkinkan pengembang untuk memperluas jenis asli Objective-C dengan cara yang sama seperti mereka akan memperluas jenis C#, menggunakan kata kunci "ambil alih" pada kelas turunan, dan menautkan ke implementasi dasar menggunakan kata kunci C# "dasar".

Desain ini memungkinkan pengembang untuk menghindari berurusan dengan Objective-C pemilih sebagai bagian dari proses pengembangan mereka, karena seluruh Objective-C sistem sudah dibungkus di dalam pustaka Xamarin.iOS.

Jenis dan Penyusun Antarmuka

Saat Anda membuat kelas .NET yang merupakan instans jenis yang dibuat oleh Interface Builder, Anda perlu menyediakan konstruktor yang mengambil satu IntPtr parameter. Ini diperlukan untuk mengikat instans objek terkelola dengan objek yang tidak dikelola. Kode terdiri dari satu baris, seperti ini:

public partial class void MyView : UIView {
   // This is the constructor that you need to add.
   public MyView (IntPtr handle) : base (handle) {}
}

Delegasikan

Objective-C dan C# memiliki arti yang berbeda untuk delegasi kata dalam setiap bahasa.

Objective-C Di dunia, dan dalam dokumentasi yang akan Anda temukan secara online tentang CocoaTouch, delegasi biasanya adalah instans kelas yang akan menanggapi serangkaian metode. Ini mirip dengan antarmuka C#, dengan perbedaan adalah bahwa metode tidak selalu wajib.

Delegasi ini memainkan peran penting dalam UIKit dan API CocoaTouch lainnya. Mereka digunakan untuk menyelesaikan berbagai tugas:

  • Untuk memberikan pemberitahuan ke kode Anda (Mirip dengan pengiriman peristiwa di C# atau Gtk+).
  • Untuk menerapkan model untuk kontrol visualisasi data.
  • Untuk mendorong perilaku kontrol.

Pola pemrograman dirancang untuk meminimalkan pembuatan kelas turunan untuk mengubah perilaku untuk kontrol. Solusi ini mirip dengan apa yang telah dilakukan toolkit GUI lainnya selama bertahun-tahun: Sinyal Gtk, slot Qt, peristiwa Winforms, peristiwa WPF/Silverlight dan sebagainya. Untuk menghindari memiliki ratusan antarmuka (satu untuk setiap tindakan) atau mengharuskan pengembang untuk menerapkan terlalu banyak metode yang tidak mereka butuhkan, Objective-C mendukung definisi metode opsional. Ini berbeda dari antarmuka C# yang mengharuskan semua metode diimplementasikan.

Di Objective-C kelas, Anda akan melihat bahwa kelas yang menggunakan pola pemrograman ini mengekspos properti, yang disebut delegate, yang diperlukan untuk mengimplementasikan bagian wajib antarmuka dan nol, atau lebih, dari bagian opsional.

Dalam Xamarin.iOS, tiga mekanisme yang saling eksklusif untuk mengikat delegasi ini ditawarkan:

  1. Melalui peristiwa.
  2. Sangat ditik melalui Delegate properti
  3. Dititik secara longgar melalui WeakDelegate properti

Misalnya, pertimbangkan kelas UIWebView. Ini dikirim ke instans UIWebViewDelegate, yang ditetapkan ke properti delegasi.

Melalui Peristiwa

Untuk banyak jenis, Xamarin.iOS akan secara otomatis membuat delegasi yang sesuai, yang akan meneruskan panggilan ke UIWebViewDelegate peristiwa C#. Untuk UIWebView:

Misalnya, program sederhana ini mencatat waktu mulai dan berakhir saat memuat tampilan web:

DateTime startTime, endTime;
var web = new UIWebView (new CGRect (0, 0, 200, 200));
web.LoadStarted += (o, e) => startTime = DateTime.Now;
web.LoadFinished += (o, e) => endTime = DateTime.Now;
Melalui Properti

Peristiwa berguna ketika mungkin ada lebih dari satu pelanggan untuk peristiwa tersebut. Selain itu, peristiwa terbatas pada kasus di mana tidak ada nilai pengembalian dari kode.

Untuk kasus di mana kode diharapkan mengembalikan nilai, kami memilih untuk properti. Ini berarti bahwa hanya satu metode yang dapat diatur pada waktu tertentu dalam objek.

Misalnya, Anda dapat menggunakan mekanisme ini untuk menutup keyboard pada layar pada handler untuk UITextField:

void SetupTextField (UITextField tf)
{
    tf.ShouldReturn = delegate (textfield) {
        textfield.ResignFirstResponder ();
        return true;
    }
}

Properti UITextField's ShouldReturn dalam hal ini mengambil sebagai argumen delegasi yang mengembalikan nilai bool dan menentukan apakah TextField harus melakukan sesuatu dengan tombol Kembalikan yang ditekan. Dalam metode kami, kami mengembalikan true ke pemanggil, tetapi kami juga menghapus keyboard dari layar (ini terjadi ketika bidang teks memanggil ResignFirstResponder).

Sangat Ditik melalui Properti Delegasi

Jika Anda lebih suka tidak menggunakan peristiwa, Anda dapat menyediakan subkelas UIWebViewDelegate Anda sendiri dan menetapkannya ke properti UIWebView.Delegate. Setelah UIWebView.Delegate ditetapkan, mekanisme pengiriman peristiwa UIWebView tidak akan berfungsi lagi, dan metode UIWebViewDelegate akan dipanggil ketika peristiwa yang sesuai terjadi.

Misalnya, jenis sederhana ini merekam waktu yang diperlukan untuk memuat tampilan web:

class Notifier : UIWebViewDelegate  {
    DateTime startTime, endTime;

    public override LoadStarted (UIWebView webview)
    {
        startTime = DateTime.Now;
    }

    public override LoadingFinished (UIWebView webView)
    {
        endTime= DateTime.Now;
    }
}

Di atas digunakan dalam kode seperti ini:

var web = new UIWebView (new CGRect (0, 0, 200, 200));
web.Delegate = new Notifier ();

Di atas akan membuat UIWebViewer dan akan menginstruksikannya untuk mengirim pesan ke instans Notifier, kelas yang kami buat untuk menanggapi pesan.

Pola ini juga digunakan untuk mengontrol perilaku untuk kontrol tertentu, misalnya dalam kasus UIWebView, properti UIWebView.ShouldStartLoad memungkinkan UIWebView instans mengontrol apakah UIWebView akan memuat halaman atau tidak.

Pola ini juga digunakan untuk menyediakan data sesuai permintaan untuk beberapa kontrol. Misalnya, kontrol UITableView adalah kontrol penyajian tabel yang kuat - dan tampilan dan konten didorong oleh instans UITableViewDataSource

Ditik secara Longgar melalui Properti WeakDelegate

Selain properti yang dititik dengan kuat, ada juga delegasi berjenis lemah yang memungkinkan pengembang untuk mengikat hal-hal secara berbeda jika diinginkan. Di mana-mana properti yang sangat ditik Delegate diekspos dalam pengikatan Xamarin.iOS, properti yang sesuai WeakDelegate juga diekspos.

Saat menggunakan WeakDelegate, Anda bertanggung jawab untuk mendekorasi kelas Anda dengan benar menggunakan atribut Ekspor untuk menentukan pemilih. Contohnya:

class Notifier : NSObject  {
    DateTime startTime, endTime;

    [Export ("webViewDidStartLoad:")]
    public void LoadStarted (UIWebView webview)
    {
        startTime = DateTime.Now;
    }

    [Export ("webViewDidFinishLoad:")]
    public void LoadingFinished (UIWebView webView)
    {
        endTime= DateTime.Now;
    }
}

[...]

var web = new UIWebView (new CGRect (0, 0, 200, 200));
web.WeakDelegate = new Notifier ();

WeakDelegate Setelah properti ditetapkan, Delegate properti tidak akan digunakan. Selain itu, jika Anda menerapkan metode dalam kelas dasar yang diwariskan yang ingin Anda [Ekspor], Anda harus menjadikannya metode publik.

Pemetaan Objective-C pola delegasi ke C#

Saat Anda melihat Objective-C sampel yang terlihat seperti ini:

foo.delegate = [[SomethingDelegate] alloc] init]

Ini menginstruksikan bahasa untuk membuat dan membuat instans kelas "SomethingDelegate" dan menetapkan nilai ke properti delegasi pada variabel foo. Mekanisme ini didukung oleh Xamarin.iOS dan C# sintaksnya adalah:

foo.Delegate = new SomethingDelegate ();

Di Xamarin.iOS, kami telah menyediakan kelas dengan jenis kuat yang memetakan ke Objective-C kelas delegasi. Untuk menggunakannya, Anda akan mensubkelas dan mengambil alih metode yang ditentukan oleh implementasi Xamarin.iOS. Untuk informasi selengkapnya tentang cara kerjanya, lihat bagian "Model" di bawah ini.

Memetakan Delegasi ke C#

UIKit secara umum menggunakan Objective-C delegasi dalam dua bentuk.

Formulir pertama menyediakan antarmuka untuk model komponen. Misalnya, sebagai mekanisme untuk menyediakan data sesuai permintaan untuk tampilan, seperti fasilitas penyimpanan data untuk tampilan Daftar. Dalam kasus ini, Anda harus selalu membuat instans kelas yang tepat dan menetapkan variabel.

Dalam contoh berikut, kami menyediakan UIPickerView implementasi untuk model yang menggunakan string:

public class SampleTitleModel : UIPickerViewTitleModel {

    public override string TitleForRow (UIPickerView picker, nint row, nint component)
    {
        return String.Format ("At {0} {1}", row, component);
    }
}

[...]

pickerView.Model = new MyPickerModel ();

Formulir kedua adalah memberikan pemberitahuan untuk peristiwa. Dalam kasus tersebut, meskipun kami masih mengekspos API dalam bentuk yang diuraikan di atas, kami juga menyediakan peristiwa C#, yang seharusnya lebih mudah digunakan untuk operasi cepat dan terintegrasi dengan delegasi anonim dan ekspresi lambda di C#.

Misalnya, Anda dapat berlangganan UIAccelerometer peristiwa:

UIAccelerometer.SharedAccelerometer.Acceleration += (sender, args) => {
   UIAcceleration acc = args.Acceleration;
   Console.WriteLine ("Time={0} at {1},{2},{3}", acc.Time, acc.X, acc.Y, acc.Z);
}

Dua opsi tersedia di mana mereka masuk akal, tetapi sebagai programmer Anda harus memilih satu atau yang lain. Jika Anda membuat instans Anda sendiri dari responden/delegasi yang sangat ditik dan menetapkannya, peristiwa C# tidak akan berfungsi. Jika Anda menggunakan peristiwa C#, metode di kelas responden/delegasi Anda tidak akan pernah dipanggil.

Contoh sebelumnya yang digunakan UIWebView dapat ditulis menggunakan C# 3.0 lambda seperti ini:

var web = new UIWebView (new CGRect (0, 0, 200, 200));
web.LoadStarted += () => { startTime = DateTime.Now; }
web.LoadFinished += () => { endTime = DateTime.Now; }

Menanggapi Peristiwa

Dalam Objective-C kode, terkadang penanganan aktivitas untuk beberapa kontrol dan penyedia informasi untuk beberapa kontrol, akan dihosting di kelas yang sama. Hal ini dimungkinkan karena kelas merespons pesan, dan selama kelas merespons pesan, dimungkinkan untuk menautkan objek bersama-sama.

Seperti yang dijelaskan sebelumnya, Xamarin.iOS mendukung model pemrograman berbasis peristiwa C#, dan Objective-C pola delegasi, di mana Anda dapat membuat kelas baru yang mengimplementasikan delegasi dan mengambil alih metode yang diinginkan.

Dimungkinkan juga untuk mendukung Objective-Cpola di mana responden untuk beberapa operasi yang berbeda semuanya dihosting dalam instans kelas yang sama. Namun, Anda harus menggunakan fitur tingkat rendah dari pengikatan Xamarin.iOS.

Misalnya, jika Anda ingin kelas Anda merespons UITextFieldDelegate.textFieldShouldClearpesan : dan UIWebViewDelegate.webViewDidStartLoad: dalam instans kelas yang sama, Anda harus menggunakan deklarasi atribut [Ekspor]:

public class MyCallbacks : NSObject {
    [Export ("textFieldShouldClear:"]
    public bool should_we_clear (UITextField tf)
    {
        return true;
    }

    [Export ("webViewDidStartLoad:")]
    public void OnWebViewStart (UIWebView view)
    {
        Console.WriteLine ("Loading started");
    }
}

Nama C# untuk metode tidak penting; yang penting adalah string yang diteruskan ke atribut [Ekspor].

Saat menggunakan gaya pemrograman ini, pastikan parameter C# cocok dengan jenis aktual yang akan dilewati mesin runtime.

Model

Di fasilitas penyimpanan UIKit, atau di responden yang diimplementasikan menggunakan kelas pembantu, ini dirujuk dalam Objective-C kode sebagai delegasi, dan diimplementasikan sebagai protokol.

Objective-C protokol seperti antarmuka, tetapi mendukung metode opsional - artinya, tidak semua metode perlu diimplementasikan agar protokol berfungsi.

Ada dua cara untuk menerapkan model. Anda dapat mengimplementasikannya secara manual atau menggunakan definisi yang ditik dengan kuat yang ada.

Mekanisme manual diperlukan ketika Anda mencoba menerapkan kelas yang belum terikat oleh Xamarin.iOS. Sangat mudah dilakukan:

  • Benderai kelas Anda untuk pendaftaran dengan runtime
  • Terapkan atribut [Ekspor] dengan nama pemilih aktual pada setiap metode yang ingin Anda ambil alih
  • Buat instans kelas, dan teruskan.

Misalnya, berikut ini hanya menerapkan salah satu metode opsional dalam definisi protokol UIApplicationDelegate:

public class MyAppController : NSObject {
        [Export ("applicationDidFinishLaunching:")]
        public void FinishedLaunching (UIApplication app)
        {
                SetupWindow ();
        }
}

Nama Objective-C pemilih ("applicationDidFinishLaunching:") dinyatakan dengan atribut Ekspor dan kelas terdaftar dengan [Register] atribut .

Xamarin.iOS menyediakan deklarasi yang sangat diketik, siap digunakan, yang tidak memerlukan pengikatan manual. Untuk mendukung model pemrograman ini, runtime Xamarin.iOS mendukung atribut [Model] pada deklarasi kelas. Ini menginformasikan runtime bahwa itu tidak boleh menghubungkan semua metode di kelas, kecuali metode diimplementasikan secara eksplisit.

Ini berarti bahwa dalam UIKit, kelas yang mewakili protokol dengan metode opsional ditulis seperti ini:

[Model]
public class SomeViewModel : NSObject {
    [Export ("someMethod:")]
    public virtual int SomeMethod (TheView view) {
       throw new ModelNotImplementedException ();
    }
    ...
}

Ketika Anda ingin menerapkan model yang hanya mengimplementasikan beberapa metode, yang harus Anda lakukan adalah mengambil alih metode yang Anda minati, dan mengabaikan metode lain. Runtime hanya akan menghubungkan metode yang ditimpa, bukan metode asli ke Objective-C dunia.

Yang setara dengan sampel manual sebelumnya adalah:

public class AppController : UIApplicationDelegate {
    public override void FinishedLaunching (UIApplication uia)
    {
     ...
    }
}

Keuntungannya adalah tidak perlu menggali Objective-C file header untuk menemukan pemilih, jenis argumen, atau pemetaan ke C#, dan bahwa Anda mendapatkan intellisense dari Visual Studio untuk Mac, bersama dengan jenis yang kuat

Outlet XIB dan C#

Ini adalah deskripsi tingkat rendah tentang bagaimana Outlet terintegrasi dengan C# dan disediakan untuk pengguna lanjutan Xamarin.iOS. Saat menggunakan Visual Studio untuk Mac, pemetaan dilakukan secara otomatis di belakang layar menggunakan kode yang dihasilkan pada penerbangan untuk Anda.

Saat merancang antarmuka pengguna dengan Interface Builder, Anda hanya akan merancang tampilan aplikasi dan akan membuat beberapa koneksi default. Jika Anda ingin mengambil informasi secara terprogram, ubah perilaku kontrol saat runtime atau ubah kontrol saat runtime, perlu untuk mengikat beberapa kontrol ke kode terkelola Anda.

Ini dilakukan dalam beberapa langkah:

  1. Tambahkan deklarasi outlet ke pemilik File Anda.
  2. Koneksi kontrol Anda ke Pemilik file.
  3. Simpan UI ditambah koneksi ke file XIB/NIB Anda.
  4. Muat file NIB saat runtime.
  5. Akses variabel outlet.

Langkah-langkah (1) hingga (3) tercakup dalam dokumentasi Apple untuk membangun antarmuka dengan Interface Builder.

Saat menggunakan Xamarin.iOS, aplikasi Anda harus membuat kelas yang berasal dari UIViewController. Ini diimplementasikan seperti ini:

public class MyViewController : UIViewController {
    public MyViewController (string nibName, NSBundle bundle) : base (nibName, bundle)
    {
        // You can have as many arguments as you want, but you need to call
        // the base constructor with the provided nibName and bundle.
    }
}

Kemudian untuk memuat ViewController Anda dari file NIB, Anda melakukan ini:

var controller = new MyViewController ("HelloWorld", NSBundle.MainBundle, this);

Ini memuat antarmuka pengguna dari NIB. Sekarang, untuk mengakses outlet, perlu untuk menginformasikan runtime bahwa kita ingin mengaksesnya. Untuk melakukan ini, UIViewController subkelas perlu mendeklarasikan properti dan membuat anotasi dengan atribut [Koneksi]. Seperti ini:

[Connect]
UITextField UserName {
    get {
        return (UITextField) GetNativeField ("UserName");
    }
    set {
        SetNativeField ("UserName", value);
    }
}

Implementasi properti adalah yang benar-benar mengambil dan menyimpan nilai untuk jenis asli yang sebenarnya.

Anda tidak perlu khawatir tentang hal ini saat menggunakan Visual Studio untuk Mac dan InterfaceBuilder. Visual Studio untuk Mac secara otomatis mencerminkan semua outlet yang dideklarasikan dengan kode di kelas parsial yang dikompilasi sebagai bagian dari proyek Anda.

Pemilih

Konsep inti pemrograman Objective-C adalah pemilih. Anda akan sering menemukan API yang mengharuskan Anda meneruskan pemilih, atau mengharapkan kode Anda merespons pemilih.

Membuat pemilih baru di C# mudah - Anda hanya membuat instans ObjCRuntime.Selector baru kelas dan menggunakan hasilnya di mana pun di API yang memerlukannya. Contohnya:

var selector_add = new Selector ("add:plus:");

Untuk metode C# merespons panggilan pemilih, metode harus mewarisi dari NSObject jenis dan metode C# harus dihiasi dengan nama pemilih menggunakan [Export] atribut . Contohnya:

public class MyMath : NSObject {
    [Export ("add:plus:")]
    int Add (int first, int second)
    {
         return first + second;
    }
}

Nama pemilih harus sama persis, termasuk semua titik dua perantara dan titik dua berikutnya (":"), jika ada.

Konstruktor NSObject

Sebagian besar kelas di Xamarin.iOS yang berasal dari NSObject akan mengekspos konstruktor khusus untuk fungsionalitas objek, tetapi mereka juga akan mengekspos berbagai konstruktor yang tidak segera jelas.

Konstruktor digunakan sebagai berikut:

public Foo (IntPtr handle)

Konstruktor ini digunakan untuk membuat instans kelas Anda saat runtime perlu memetakan kelas Anda ke kelas yang tidak dikelola. Ini terjadi ketika Anda memuat file XIB/NIB. Pada titik ini, Objective-C runtime akan membuat objek di dunia yang tidak dikelola, dan konstruktor ini akan dipanggil untuk menginisialisasi sisi terkelola.

Biasanya, yang perlu Anda lakukan adalah memanggil konstruktor dasar dengan parameter handel, dan dalam isinya, lakukan inisialisasi apa pun yang diperlukan.

public Foo ()

Ini adalah konstruktor default untuk kelas, dan di kelas yang disediakan Xamarin.iOS, ini menginisialisasi kelas Foundation.NSObject dan semua kelas di antaranya, dan di akhir, menautkan ini ke Objective-Cinit metode pada kelas.

public Foo (NSObjectFlag x)

Konstruktor ini digunakan untuk menginisialisasi instans, tetapi mencegah kode memanggil Objective-C metode "init" di akhir. Anda biasanya menggunakan ini ketika Anda sudah mendaftar untuk inisialisasi (ketika Anda menggunakan [Export] pada konstruktor Anda) atau ketika Anda telah melakukan inisialisasi Anda melalui rata-rata lain.

public Foo (NSCoder coder)

Konstruktor ini disediakan untuk kasus di mana objek sedang diinisialisasi dari instans NSCoding.

Pengecualian

Desain Xamarin.iOS API tidak menimbulkan Objective-C pengecualian sebagai pengecualian C#. Desain ini memberlakukan bahwa tidak ada sampah yang dikirim ke Objective-C dunia pada awalnya dan bahwa pengecualian apa pun yang harus diproduksi diproduksi oleh pengikatan itu sendiri sebelum data yang tidak valid pernah diteruskan ke Objective-C dunia.

Pemberitahuan

Di iOS dan OS X, pengembang dapat berlangganan pemberitahuan yang disiarkan oleh platform yang mendasar. Ini dilakukan dengan menggunakan NSNotificationCenter.DefaultCenter.AddObserver metode . Metode ini AddObserver mengambil dua parameter; satu adalah pemberitahuan yang ingin Anda berlangganan; yang lain adalah metode yang akan dipanggil ketika pemberitahuan dinaikkan.

Di Xamarin.iOS dan Xamarin.Mac, kunci untuk berbagai pemberitahuan dihosting di kelas yang memicu pemberitahuan. Misalnya, pemberitahuan yang dimunculkan oleh UIMenuController dihosting sebagai static NSString properti di UIMenuController kelas yang berakhiran dengan nama "Pemberitahuan".

Manajemen Memori

Xamarin.iOS memiliki pengumpul sampah yang akan mengurus melepaskan sumber daya untuk Anda ketika tidak lagi digunakan. Selain pengumpul sampah, semua objek yang berasal dari NSObject mengimplementasikan System.IDisposable antarmuka.

NSObject dan IDisposable

Mengekspos IDisposable antarmuka adalah cara mudah untuk membantu pengembang dalam merilis objek yang mungkin merangkum blok memori yang besar (misalnya, UIImage mungkin terlihat seperti hanya penunjuk yang tidak bersalah, tetapi bisa menunjuk ke gambar 2 megabyte) dan sumber daya penting dan terbatas lainnya (seperti buffer decoding video).

NSObject mengimplementasikan antarmuka IDisposable dan juga pola .NET Dispose. Ini memungkinkan pengembang yang subkelas NSObject untuk mengambil alih perilaku Buang dan merilis sumber daya mereka sendiri sesuai permintaan. Misalnya, pertimbangkan pengontrol tampilan ini yang menyimpan banyak gambar:

class MenuViewController : UIViewController {
    UIImage breakfast, lunch, dinner;
    [...]
    public override void Dispose (bool disposing)
    {
        if (disposing){
             if (breakfast != null) breakfast.Dispose (); breakfast = null;
             if (lunch != null) lunch.Dispose (); lunch = null;
             if (dinner != null) dinner.Dispose (); dinner = null;
        }
        base.Dispose (disposing)
    }
}

Ketika objek terkelola dibuang, objek tersebut tidak lagi berguna. Anda mungkin masih memiliki referensi ke objek, tetapi objek untuk semua niat dan tujuan tidak valid pada saat ini. Beberapa API .NET memastikan ini dengan melempar ObjectDisposedException jika Anda mencoba mengakses metode apa pun pada objek yang dibuang, misalnya:

var image = UIImage.FromFile ("demo.png");
image.Dispose ();
image.XXX = false;  // this at this point is an invalid operation

Bahkan jika Anda masih dapat mengakses variabel "gambar", itu benar-benar referensi yang tidak valid dan tidak lagi menunjuk ke Objective-C objek yang memegang gambar.

Tetapi membuang objek di C# tidak berarti bahwa objek tersebut akan selalu dihancurkan. Yang Anda lakukan adalah merilis referensi yang C# miliki ke objek . Ada kemungkinan bahwa lingkungan Kakao mungkin telah menyimpan referensi untuk penggunaannya sendiri. Misalnya, jika Anda mengatur properti Gambar UIImageView ke gambar, lalu Anda membuang gambar, UIImageView yang mendasar telah mengambil referensinya sendiri dan akan menyimpan referensi ke objek ini sampai selesai menggunakannya.

Kapan harus memanggil Buang

Hubungi Buang ketika Anda membutuhkan Mono untuk menyingkirkan objek Anda. Kemungkinan kasus penggunaan adalah ketika Mono tidak memiliki pengetahuan bahwa NSObject Anda benar-benar menyimpan referensi ke sumber daya penting seperti memori, atau kumpulan informasi. Dalam kasus tersebut, Anda harus memanggil Dispose untuk segera merilis referensi ke memori, alih-alih menunggu Mono melakukan siklus pengumpulan sampah.

Secara internal, ketika Mono membuat referensi NSString dari string C#, Mono akan segera membuangnya untuk mengurangi jumlah pekerjaan yang harus dilakukan pengumpul sampah. Semakin sedikit objek di sekitar untuk ditangani, semakin cepat GC akan berjalan.

Kapan Menyimpan Referensi ke Objek

Salah satu efek samping yang dimiliki manajemen memori otomatis adalah bahwa GC akan menyingkirkan objek yang tidak digunakan selama tidak ada referensi untuk mereka. Yang terkadang dapat memiliki efek samping yang mengejutkan, misalnya, jika Anda membuat variabel lokal untuk menahan pengontrol tampilan tingkat atas Anda, atau jendela tingkat atas Anda, dan kemudian memiliki mereka lenyap di belakang punggung Anda.

Jika Anda tidak menyimpan referensi dalam variabel statis atau instans Anda ke objek Anda, Mono akan dengan senang hati memanggil metode Dispose() pada mereka, dan mereka akan merilis referensi ke objek. Karena ini mungkin satu-satunya referensi yang luar biasa, Objective-C runtime akan menghancurkan objek untuk Anda.