Návrh rozhraní API Xamarin.iOS
Kromě základních knihoven základních tříd, které jsou součástí Mono, se Xamarin.iOS dodává s vazbami pro různá rozhraní API pro iOS, aby vývojáři mohli vytvářet nativní aplikace pro iOS pomocí Mono.
Základem Xamarin.iOS je interopový modul, který přemostí svět C# se světem Objective-C a vazby pro rozhraní API založená na jazyce iOS, jako jsou CoreGraphics a OpenGL ES.
Modul runtime nízké úrovně pro komunikaci s kódem Objective-C je v MonoTouch.ObjCRuntime. Dále jsou k dispozici vazby pro Foundation, CoreFoundation a UIKit .
Principy návrhu
Tato část podrobně popisuje některé principy návrhu pro vazby Xamarin.iOS (platí také pro Xamarin.Mac, mono vazby pro Objective-C macOS):
Postupujte podle pokynů pro návrh architektury.
Povolit vývojářům podtřídy Objective-C tříd:
- Odvození z existující třídy
- Volání základního konstruktoru ke řetězení
- Přepsání metod by se mělo provádět v systému přepsání jazyka C#.
- Podtřídy by měly fungovat se standardními konstrukty jazyka C#
Nezpřístupňujte vývojáře Objective-C selektorům
Poskytnutí mechanismu pro volání libovolných Objective-C knihoven
Usnadnit a usnadnit Objective-C běžné Objective-C úkoly
Zveřejnění Objective-C vlastností jako vlastností jazyka C#
Zveřejnění rozhraní API silného typu:
- Zvýšení bezpečnosti typů
- Minimalizace chyb modulu runtime
- Získání technologie IDE IntelliSense pro návratové typy
- Umožňuje místní dokumentaci k integrovanému vývojovému prostředí (IDE).
Podpora zkoumání rozhraní API v integrovaném vývojovém prostředí (IDE):
Například místo vystavení slabě napsaného pole, například takto:
NSArray *getViews
Zpřístupňte silný typ, například takto:
NSView [] Views { get; set; }
Použití silných typů poskytuje Visual Studio pro Mac možnost automatického dokončování při procházení rozhraní API, zpřístupní všechny
System.Array
operace na vrácené hodnotě a umožní návratové hodnotě účastnit se LINQ.
Nativní typy jazyka C#:
Převod
int
auint
parametry, které by měly být výčty do výčtů jazyka C# a výčtů jazyka C# s[Flags]
atributyMísto objektů neutrálních
NSArray
typu zpřístupňujte pole jako pole se silnými typy.U událostí a oznámení dejte uživatelům možnost výběru mezi:
- Ve výchozím nastavení je verze silného typu
- Slabě napsaná verze pro pokročilé případy použití
Podpora vzoru delegáta Objective-C :
- Systém událostí jazyka C#
- Zveřejnění delegátů jazyka C# (lambda, anonymní metody a
System.Delegate
) pro Objective-C rozhraní API jako bloky
Sestavení
Xamarin.iOS obsahuje mnoho sestavení, která tvoří profil Xamarin.iOS. Stránka Sestavení obsahuje další informace.
Hlavní obory názvů
ObjCRuntime
Obor názvů ObjCRuntime umožňuje vývojářům přemostit světy mezi jazykem C# a Objective-C. Jedná se o novou vazbu navrženou speciálně pro iOS na základě prostředí z Cocoa# a Gtk#.
Nadace
Základní obor názvů poskytuje základní datové typy navržené pro spolupráci s architekturou Objective-C Foundation, která je součástí iOS a je základem pro objektově orientované programování v Objective-C.
Xamarin.iOS zrcadlí v jazyce C# hierarchii tříd z Objective-C. Například základní Objective-C třída NSObject je použitelná z jazyka C# prostřednictvím Foundation.NSObject.
Přestože obor názvů Foundation poskytuje vazby pro základní Objective-C typy Foundation, v několika případech jsme namapovali základní typy na typy .NET. Příklad:
Místo práce s NSString a NSArray je modul runtime zveřejňuje jako řetězceC# a polesilného typu v celém rozhraní API.
Různá pomocná rozhraní API jsou zde zpřístupněna, aby vývojáři mohli svázat rozhraní API třetích stran Objective-C , jiná rozhraní API pro iOS nebo rozhraní API, která nejsou aktuálně vázána Xamarin.iOS.
Další informace o rozhraních API vazeb najdete v části Generátor vazeb Xamarin.iOS.
NSObject
Typ NSObject je základem všech Objective-C vazeb. Typy Xamarin.iOS zrcadlí dvě třídy typů z rozhraní API CocoaTouch pro iOS: typy jazyka C (obvykle označované jako typy CoreFoundation) a Objective-C typy (které jsou odvozeny ze třídy NSObject).
Pro každý typ, který zrcadlí nespravovaný typ, je možné získat nativní objekt prostřednictvím Handle vlastnost.
Zatímco Mono poskytne uvolňování paměti pro všechny objekty, Foundation.NSObject
implementuje System.IDisposable rozhraní. Prostředky libovolného objektu NSObject můžete explicitně uvolnit, aniž byste museli čekat na spuštění uvolňování paměti. Explicitní uvolnění prostředků je důležité, když používáte náročné objekty NSObject, například UIImages, které můžou obsahovat ukazatele na velké bloky dat.
Pokud váš typ potřebuje provést deterministické finalizace, přepište NSObject.Dispose(bool) metoda Parametr Dispose je "bool disposing" a pokud je nastavena na hodnotu true, znamená to, že metoda Dispose je volána, protože uživatel explicitně volal Dispose () u objektu. Hodnota false znamená, že metoda Dispose(bool disposing) je volána z finalizátoru ve vlákně finalizátoru.
Kategorie
Od Xamarin.iOS 8.10 je možné vytvořit Objective-C kategorie z jazyka C#.
To se provádí pomocí atributu Category
, který určuje typ, který se má rozšířit jako argument atributu. Následující příklad bude například rozšířit NSString.
[Category (typeof (NSString))]
Každá metoda kategorie používá normální mechanismus pro export metod pomocí Objective-C atributu Export
:
[Export ("today")]
public static string Today ()
{
return "Today";
}
Všechny spravované metody rozšíření musí být statické, ale je možné vytvořit Objective-C metody instance pomocí standardní syntaxe pro rozšiřující metody v jazyce C#:
[Export ("toUpper")]
public static string ToUpper (this NSString self)
{
return self.ToString ().ToUpper ();
}
a prvním argumentem metody rozšíření bude instance, na které byla metoda vyvolána.
Kompletní příklad:
[Category (typeof (NSString))]
public static class MyStringCategory
{
[Export ("toUpper")]
static string ToUpper (this NSString self)
{
return self.ToString ().ToUpper ();
}
}
Tento příklad přidá nativní toUpper instance metoda do NSString třídy, kterou lze vyvolat z Objective-C.
[Category (typeof (UIViewController))]
public static class MyViewControllerCategory
{
[Export ("shouldAutoRotate")]
static bool GlobalRotate ()
{
return true;
}
}
Jedním ze scénářů, kdy je to užitečné, je přidání metody do celé sady tříd v základu kódu, například by se všechny UIViewController
instance sestavy, které mohou otočit:
[Category (typeof (UINavigationController))]
class Rotation_IOS6 {
[Export ("shouldAutorotate:")]
static bool ShouldAutoRotate (this UINavigationController self)
{
return true;
}
}
PreserveAttribute
PreserveAttribute je vlastní atribut, který se používá k tomu, aby nástroj pro nasazení Xamarin.iOS – zachoval typ nebo člena typu během fáze zpracování aplikace, aby se snížila jeho velikost.
Každý člen, který není staticky propojený aplikací, se může odebrat. Tento atribut se proto používá k označení členů, na které se staticky neodkazují, ale které vaše aplikace stále potřebuje.
Pokud například vytváříte instance typů dynamicky, můžete chtít zachovat výchozí konstruktor vašich typů. Pokud používáte serializaci XML, můžete chtít zachovat vlastnosti vašich typů.
Tento atribut můžete použít u každého člena typu nebo u samotného typu. Pokud chcete zachovat celý typ, můžete u typu použít syntaxi [Preserve (AllMembers = true)].
UIKit
Obor názvů UIKit obsahuje mapování 1:1 na všechny komponenty uživatelského rozhraní, které tvoří CocoaTouch ve formě tříd C#. Rozhraní API bylo upraveno tak, aby dodržovalo konvence používané v jazyce C#.
Delegáti jazyka C# jsou k dispozici pro běžné operace. Další informace najdete v části Delegáti .
OpenGLES
Pro OpenGLES distribuujeme upravenou verzi rozhraní OpenTK API, objektově orientované vazby na OpenGL, která byla upravena tak, aby používala datové typy a struktury CoreGraphics, a zobrazujeme pouze funkce, které jsou k dispozici v iOSu.
Funkce OpenGLES 1.1 je dostupná prostřednictvím typu ES11.GL.
Funkce OpenGLES 2.0 je dostupná prostřednictvím typu ES20.GL.
Funkce OpenGLES 3.0 je dostupná prostřednictvím typu ES30.GL.
Návrh vazby
Xamarin.iOS není pouze vazbou na podkladovou Objective-C platformu. Rozšiřuje systém typů .NET a odesílá systém tak, aby lépe prolážil jazyk C# a Objective-C.
Stejně jako P/Invoke je užitečný nástroj pro vyvolání nativních knihoven ve Windows a Linuxu nebo jako podpora IJW lze použít pro zprostředkovatele komunikace modelu COM ve Windows, Xamarin.iOS rozšiřuje modul runtime tak, aby podporoval vazby objektů C# na Objective-C objekty.
Diskuze v následujících několika částech není nutná pro uživatele, kteří vytvářejí aplikace Xamarin.iOS, ale pomůže vývojářům pochopit, jak se to dělá, a pomůže jim při vytváření složitějších aplikací.
Typy
Tam, kde to dává smysl, jsou typy C# vystaveny místo základních typů nízké úrovně do vesmíru jazyka C#. To znamená, že rozhraní API používá místo NSString typ "string" jazyka C# a místo vystavení NSArray používá pole jazyka C# se silnými typy.
Obecně platí, že v návrhu Xamarin.iOS a Xamarin.Mac se podkladový NSArray
objekt nezpřístupní. Místo toho modul runtime automaticky převede NSArray
pole se silnými typy některých NSObject
tříd. Xamarin.iOS tedy nezpřístupňuje slabě zapisovanou metodu, jako je GetViews, aby vrátila NSArray:
NSArray GetViews ();
Místo toho vazba zveřejňuje návratovou hodnotu silného typu, například takto:
UIView [] GetViews ();
Existuje několik metod vystavených v NSArray
rohu případů, kdy můžete chtít použít NSArray
přímo, ale jejich použití se nedoporučuje ve vazbě rozhraní API.
Kromě toho jsme je v klasickém rozhraní API místo vystavení CGRect
CGPoint
a CGSize
z rozhraní CoreGraphics API nahradili System.Drawing
implementacemi RectangleF
a PointF
SizeF
pomohli vývojářům zachovat stávající kód OpenGL, který používá OpenTK. Při použití nového 64bitového sjednoceného rozhraní API by se mělo použít rozhraní CoreGraphics API.
Dědičnost
Návrh rozhraní API Xamarin.iOS umožňuje vývojářům rozšířit nativní Objective-C typy stejným způsobem, jako by rozšířili typ jazyka C#, pomocí klíčového slova "override" v odvozené třídě a zřetězili základní implementaci pomocí klíčového slova "base" jazyka C#.
Tento návrh umožňuje vývojářům vyhnout selektorům Objective-C v rámci procesu vývoje, protože celý Objective-C systém je už zabalený uvnitř knihoven Xamarin.iOS.
Typy a tvůrce rozhraní
Při vytváření tříd .NET, které jsou instancemi typů vytvořených Tvůrcem rozhraní, je nutné zadat konstruktor, který přebírá jeden IntPtr
parametr.
To je nutné k vytvoření vazby instance spravovaného objektu s nespravovaným objektem.
Kód se skládá z jednoho řádku, například tohoto:
public partial class void MyView : UIView {
// This is the constructor that you need to add.
public MyView (IntPtr handle) : base (handle) {}
}
Delegáti
Objective-C a jazyk C# mají různé významy pro delegáta slova v každém jazyce.
Objective-C Na světě a v dokumentaci, kterou najdete online o CocoaTouch, delegát je obvykle instance třídy, která bude reagovat na sadu metod. Podobá se rozhraní jazyka C#, přičemž rozdíl je v tom, že metody nejsou vždy povinné.
Tito delegáti hrají důležitou roli v UIKitu a dalších rozhraních API CocoaTouch. Slouží k provádění různých úloh:
- Poskytnutí oznámení vašemu kódu (podobně jako doručování událostí v jazyce C# nebo Gtk+).
- Implementace modelů pro ovládací prvky vizualizace dat
- Řízení chování ovládacího prvku.
Programovací vzor byl navržen tak, aby minimalizoval vytváření odvozených tříd pro změnu chování ovládacího prvku. Toto řešení je podobné tomu, co v průběhu let udělaly další sady nástrojů grafického uživatelského rozhraní: signály Gtk, sloty Qt, události Winforms, události WPF/Silverlight atd. Abyste se vyhnuli stovkám rozhraní (jedna pro každou akci) nebo aby vývojáři museli implementovat příliš mnoho metod, které nepotřebují, Objective-C podporuje volitelné definice metod. Toto se liší od rozhraní jazyka C#, která vyžadují implementaci všech metod.
Ve Objective-C třídách uvidíte, že třídy, které používají tento programovací vzor, zpřístupňují vlastnost, která delegate
je nutná k implementaci povinných částí rozhraní a nula nebo více volitelných částí.
V Xamarin.iOS jsou nabízeny tři vzájemně se vylučující mechanismy, které se sváže s těmito delegáty:
- Prostřednictvím událostí.
- Silné typy prostřednictvím
Delegate
vlastnosti - Volně napsané prostřednictvím
WeakDelegate
vlastnosti
Představte si například třídu UIWebView. Tento příkaz odešle do UIWebViewDelegate instance, která je přiřazena delegát vlastnost.
Prostřednictvím událostí
U mnoha typů Xamarin.iOS automaticky vytvoří odpovídající delegáta, který přesměruje UIWebViewDelegate
volání na události jazyka C#. Pro UIWebView
:
- Metoda webViewDidStartLoad je mapována na událost UIWebView.LoadStarted .
- Metoda webViewDidFinishLoad je mapována na UDÁLOST UIWebView.LoadFinished .
- Metoda webView:didFailLoadWithError je mapována na událost UIWebView.LoadError .
Například tento jednoduchý program zaznamenává časy spuštění a ukončení při načítání webového zobrazení:
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;
Přes vlastnosti
Události jsou užitečné v případě, že událost může být více než jeden odběratel události. Události jsou také omezené na případy, kdy neexistuje žádná návratová hodnota z kódu.
V případech, kdy se očekává, že kód vrátí hodnotu, jsme místo toho zvolili vlastnosti. To znamená, že v objektu lze nastavit pouze jednu metodu v daném čase.
Tento mechanismus můžete například použít k zavření klávesnice na obrazovce na obslužné rutině UITextField
pro :
void SetupTextField (UITextField tf)
{
tf.ShouldReturn = delegate (textfield) {
textfield.ResignFirstResponder ();
return true;
}
}
ShouldReturn
Vlastnost UITextField
v tomto případě přebírá jako argument delegát, který vrací logickou hodnotu a určuje, zda má TextField udělat něco s tlačítkem Return, které je stisknuto. V naší metodě se vrátíme true volajícímu, ale také odebereme klávesnici z obrazovky (k tomu dochází při volání ResignFirstResponder
textového pole ).
Silné typy prostřednictvím vlastnosti delegáta
Pokud nechcete používat události, můžete zadat vlastní podtřídu UIWebViewDelegate a přiřadit ji k UIWebView.Delegate vlastnost. Po přiřazení UIWebView.Delegate nebude mechanismus odesílání událostí UIWebView fungovat a metody UIWebViewDelegate budou vyvolány při výskytu odpovídajících událostí.
Tento jednoduchý typ například zaznamenává dobu potřebnou k načtení webového zobrazení:
class Notifier : UIWebViewDelegate {
DateTime startTime, endTime;
public override LoadStarted (UIWebView webview)
{
startTime = DateTime.Now;
}
public override LoadingFinished (UIWebView webView)
{
endTime= DateTime.Now;
}
}
Výše uvedené informace se používají v kódu takto:
var web = new UIWebView (new CGRect (0, 0, 200, 200));
web.Delegate = new Notifier ();
Výše uvedený příkaz vytvoří UIWebViewer a dá pokyn, aby odesílal zprávy do instance Notifier, třídy, kterou jsme vytvořili pro reakci na zprávy.
Tento vzor se také používá k řízení chování určitých ovládacích prvků, například v případě UIWebView, UIWebView.ShouldStartLoad vlastnost umožňuje UIWebView
instanci řídit, zda UIWebView
bude načtena stránka, nebo ne.
Vzor se také používá k poskytování dat na vyžádání pro několik ovládacích prvků. Například UITableView je výkonný ovládací prvek pro vykreslování tabulek – vzhled i obsah jsou řízeny instancí UITableViewDataSource.
Volně napsané prostřednictvím vlastnosti WeakDelegate
Kromě vlastnosti silného typu je k dispozici také slabý typ delegát, který umožňuje vývojáři svázat věci jinak, pokud je to žádoucí.
Všude, kde je vlastnost silného typu Delegate
vystavena ve vazbě Xamarin.iOS, je také vystavena odpovídající WeakDelegate
vlastnost.
Při použití WeakDelegate
, jste zodpovědní za správné dekódování třídy pomocí export atributu určit selektor. Příklad:
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
Po přiřazení Delegate
vlastnosti se vlastnost nepoužije. Pokud navíc implementujete metodu v zděděné základní třídě, kterou chcete [Exportovat], musíte ji nastavit jako veřejnou metodu.
Mapování vzoru delegáta Objective-C na C#
Když uvidíte Objective-C ukázky, které vypadají takto:
foo.delegate = [[SomethingDelegate] alloc] init]
Tím dáváte jazyku pokyn, aby vytvořil a vytvořil instanci třídy "SomethingDelegate" a přiřaďte hodnotu vlastnosti delegáta proměnné foo. Tento mechanismus podporuje Xamarin.iOS a C# syntaxe:
foo.Delegate = new SomethingDelegate ();
V Xamarin.iOS jsme poskytli třídy silného Objective-C typu, které se mapuje na delegované třídy. Chcete-li je použít, budete podtřídy a přepsání metod definovaných implementací Xamarin.iOS. Další informace o tom, jak fungují, najdete v části Modely níže.
Mapování delegátů na C#
UiKit obecně používá Objective-C delegáty ve dvou formách.
První formulář poskytuje rozhraní modelu komponenty. Například jako mechanismus pro poskytování dat na vyžádání pro zobrazení, jako je například zařízení úložiště dat pro zobrazení seznamu. V těchto případech byste měli vždy vytvořit instanci správné třídy a přiřadit proměnnou.
V následujícím příkladu poskytujeme implementaci UIPickerView
modelu, který používá řetězce:
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 ();
Druhým formulářem je poskytnutí oznámení pro události. V těchto případech sice rozhraní API stále zveřejňujeme ve výše uvedeném formuláři, ale poskytujeme také události jazyka C#, které by měly být jednodušší pro rychlé operace a integrované s anonymními delegáty a výrazy lambda v jazyce C#.
Můžete se například přihlásit k odběru UIAccelerometer
událostí:
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);
}
Tyto dvě možnosti jsou k dispozici tam, kde mají smysl, ale jako programátor musíte vybrat jednu nebo druhou. Pokud vytvoříte vlastní instanci respondéru nebo delegáta silného typu a přiřadíte ji, události jazyka C# nebudou funkční. Pokud použijete události jazyka C#, metody ve třídě respondéru nebo delegáta se nikdy nebudou volat.
Předchozí příklad, který se používá UIWebView
, lze napsat pomocí lambda C# 3.0 takto:
var web = new UIWebView (new CGRect (0, 0, 200, 200));
web.LoadStarted += () => { startTime = DateTime.Now; }
web.LoadFinished += () => { endTime = DateTime.Now; }
Reakce na události
V Objective-C kódu se někdy obslužné rutiny událostí pro více ovládacích prvků a zprostředkovatelů informací pro více ovládacích prvků hostí ve stejné třídě. To je možné, protože třídy reagují na zprávy a pokud třídy reagují na zprávy, je možné propojit objekty dohromady.
Jak bylo uvedeno výše, Xamarin.iOS podporuje programovací model založený na událostech jazyka C# i model delegáta Objective-C , kde můžete vytvořit novou třídu, která implementuje delegáta a přepíše požadované metody.
Je také možné podporovat Objective-Cvzor, kdy respondenti pro více různých operací jsou hostovaní ve stejné instanci třídy. K tomu ale budete muset použít funkce nízké úrovně vazby Xamarin.iOS.
Pokud byste například chtěli, aby třída odpověděla na UITextFieldDelegate.textFieldShouldClear
zprávu : i UIWebViewDelegate.webViewDidStartLoad
na : ve stejné instanci třídy, museli byste použít deklaraci atributu [Export]:
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");
}
}
Názvy jazyka C# pro metody nejsou důležité; všechny důležité jsou řetězce předané atributu [Export].
Při použití tohoto stylu programování se ujistěte, že parametry jazyka C# odpovídají skutečným typům, které modul runtime předá.
Modely
V zařízeních úložiště UIKit nebo v respondérech implementovaných pomocí pomocných tříd se na ně odkazuje v Objective-C kódu jako delegáti a implementují se jako protokoly.
Objective-C protokoly jsou jako rozhraní, ale podporují volitelné metody – to znamená, že není nutné implementovat všechny metody, aby protokol fungoval.
Existují dva způsoby implementace modelu. Můžete ho buď implementovat ručně, nebo použít existující definice silného typu.
Ruční mechanismus je nutný při pokusu o implementaci třídy, která nebyla vázána Xamarin.iOS. Je to snadné:
- Označení třídy příznakem pro registraci v modulu runtime
- Použití atributu [Export] se skutečným názvem selektoru u každé metody, kterou chcete přepsat
- Vytvořte instanci třídy a předejte ji.
Například následující implementace pouze jedné z volitelných metod v definici protokolu UIApplicationDelegate:
public class MyAppController : NSObject {
[Export ("applicationDidFinishLaunching:")]
public void FinishedLaunching (UIApplication app)
{
SetupWindow ();
}
}
Název Objective-C selektoru ("applicationDidFinishLaunching:") je deklarován atributem Export a třída je registrována s atributem [Register]
.
Xamarin.iOS poskytuje deklarace silného typu připravené k použití, které nevyžadují ruční vazbu. Pro podporu tohoto programovacího modelu modul runtime Xamarin.iOS podporuje atribut [Model] v deklaraci třídy. To informuje modul runtime, že by neměl zavádět všechny metody ve třídě, pokud nejsou metody explicitně implementovány.
To znamená, že třídy, které představují protokol s volitelnými metodami, se v UIKitu zapisují takto:
[Model]
public class SomeViewModel : NSObject {
[Export ("someMethod:")]
public virtual int SomeMethod (TheView view) {
throw new ModelNotImplementedException ();
}
...
}
Pokud chcete implementovat model, který implementuje pouze některé z těchto metod, stačí přepsat metody, které vás zajímají, a ignorovat ostatní metody. Modul runtime připojí pouze přepsané metody, nikoli původní metody do Objective-C světa.
Ekvivalentem předchozího ručního vzorku je:
public class AppController : UIApplicationDelegate {
public override void FinishedLaunching (UIApplication uia)
{
...
}
}
Výhodou je, že není potřeba se zabývat Objective-C soubory hlaviček a najít selektor, typy argumentů nebo mapování na jazyk C# a získat intellisense z Visual Studio pro Mac spolu se silnými typy.
Výstupy XIB a C#
Toto je popis toho, jak se zásuvky integrují s jazykem C# a poskytují se pokročilým uživatelům Xamarin.iOS. Při použití Visual Studio pro Mac se mapování provádí automaticky na pozadí pomocí vygenerovaného kódu ve testovací verzi za vás.
Při návrhu uživatelského rozhraní pomocí Tvůrce rozhraní budete navrhovat pouze vzhled aplikace a navážete některá výchozí připojení. Pokud chcete načíst informace prostřednictvím kódu programu, změnit chování ovládacího prvku za běhu nebo změnit ovládací prvek za běhu, je nutné svázat některé ovládací prvky se spravovaným kódem.
To se provádí v několika krocích:
- Přidejte deklaraci výstupu k vlastníkovi souboru.
- Připojení ovládací prvek Vlastník souboru.
- Uložte uživatelské rozhraní a připojení do souboru XIB/NIB.
- Načtěte soubor NIB za běhu.
- Přístup k výstupní proměnné
Kroky (1) až (3) jsou popsané v dokumentaci společnosti Apple k vytváření rozhraní pomocí Tvůrce rozhraní.
Při použití Xamarin.iOS bude vaše aplikace muset vytvořit třídu, která je odvozena z UIViewController. Implementuje se takto:
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.
}
}
Pak načtěte ViewController ze souboru NIB, postupujte takto:
var controller = new MyViewController ("HelloWorld", NSBundle.MainBundle, this);
Tím se uživatelské rozhraní načte z NIB. Pro přístup k výstupům je teď nutné informovat modul runtime, že k nim chceme přistupovat. K tomu UIViewController
musí podtřída deklarovat vlastnosti a anotovat je atributem [Připojení]. Nějak tak:
[Connect]
UITextField UserName {
get {
return (UITextField) GetNativeField ("UserName");
}
set {
SetNativeField ("UserName", value);
}
}
Implementace vlastnosti je ta, která ve skutečnosti načítá a ukládá hodnotu pro skutečný nativní typ.
Při použití Visual Studio pro Mac a InterfaceBuilderu se o to nemusíte starat. Visual Studio pro Mac automaticky zrcadlí všechny deklarované výstupy s kódem v částečné třídě, která je zkompilována jako součást projektu.
Voliče
Základním konceptem Objective-C programování jsou selektory. Často se setkáte s rozhraními API, která vyžadují předání selektoru nebo očekává, že váš kód odpoví na selektor.
Vytváření nových selektorů v jazyce C# je snadné – stačí vytvořit novou instanci ObjCRuntime.Selector
třídy a použít výsledek na libovolném místě v rozhraní API, které ho vyžaduje. Příklad:
var selector_add = new Selector ("add:plus:");
Pro metodu jazyka C# reagující na volání selektoru musí dědit z NSObject
typu a metoda jazyka C# musí být zdobena názvem selektoru pomocí atributu [Export]
. Příklad:
public class MyMath : NSObject {
[Export ("add:plus:")]
int Add (int first, int second)
{
return first + second;
}
}
Názvy selektoru se musí přesně shodovat, včetně všech přechodných a koncových dvojtečky (":"), pokud jsou k dispozici.
NSObject – konstruktory
Většina tříd v Xamarin.iOS, které jsou odvozeny z NSObject
, zveřejňuje konstruktory specifické pro funkce objektu, ale také zveřejní různé konstruktory, které nejsou okamžitě zřejmé.
Konstruktory se používají takto:
public Foo (IntPtr handle)
Tento konstruktor slouží k vytvoření instance třídy, když modul runtime potřebuje namapovat třídu na nespravovanou třídu. K tomu dochází při načtení souboru XIB/NIB. V tomto okamžiku Objective-C modul runtime vytvoří objekt v nespravovaném světě a tento konstruktor se zavolá k inicializaci spravované strany.
Obvykle stačí zavolat základní konstruktor s parametrem popisovače a v těle provést jakoukoli inicializaci, která je nutná.
public Foo ()
Toto je výchozí konstruktor pro třídu a v Xamarin.iOS poskytované třídy, tím inicializuje Foundation.NSObject třídy a všechny třídy mezi a na konci, zřetězí to s metodou Objective-Cinit
třídy.
public Foo (NSObjectFlag x)
Tento konstruktor se používá k inicializaci instance, ale zabránit kódu v volání Objective-C "init" metoda na konci. Obvykle to použijete, pokud jste již zaregistrovali k inicializaci (při použití [Export]
v konstruktoru) nebo když jste už inicializaci provedli jiným průměrem.
public Foo (NSCoder coder)
Tento konstruktor je poskytován pro případy, kdy je objekt inicializován z NSCoding instance.
Výjimky
Návrh rozhraní API Xamarin.iOS nevyvolá Objective-C výjimky jako výjimky jazyka C#. Návrh vynucuje, aby se na světě na prvním místě neposílaly Objective-C žádné odpadky a že všechny výjimky, které je nutné vytvořit, jsou vytvářeny samotnou vazbou předtím, než se do světa přenesou Objective-C neplatná data.
Notifications
V iOSu i OS X se vývojáři můžou přihlásit k odběru oznámení, která jsou vysíláná základní platformou. To se provádí pomocí NSNotificationCenter.DefaultCenter.AddObserver
metody. Metoda AddObserver
má dva parametry, jedno je oznámení, které chcete přihlásit k odběru. Druhá je metoda, která se má vyvolat při vyvolání oznámení.
V Xamarin.iOS i Xamarin.Mac jsou klíče různých oznámení hostované ve třídě, která oznámení aktivuje. Například oznámení vyvolaná objektem UIMenuController
jsou hostována jako static NSString
vlastnosti ve UIMenuController
třídách, které končí názvem "Oznámení".
Správa paměti
Xamarin.iOS má systém uvolňování paměti, který se postará o uvolnění prostředků za vás, když se už nepoužívají. Kromě uvolňování paměti jsou všechny objekty, které jsou odvozeny od NSObject
implementace System.IDisposable
rozhraní.
NSObject a IDisposable
IDisposable
Zveřejnění rozhraní je pohodlný způsob, jak vývojářům pomoct s uvolněním objektů, které by mohly zapouzdřit velké bloky paměti (například UIImage
může vypadat jako jen nevinný ukazatel, ale může odkazovat na 2 megabajtový obrázek) a další důležité a konečné prostředky (například vyrovnávací paměť dekódování videa).
NSObject implementuje rozhraní IDisposable a také model .NET Dispose. To umožňuje vývojářům, kteří podtřídu NSObject přepíší chování Dispose a uvolní své vlastní prostředky na vyžádání. Představte si například tento kontroler zobrazení, který se drží kolem skupiny obrázků:
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)
}
}
Když je spravovaný objekt odstraněn, už není užitečný. Stále můžete mít odkaz na objekty, ale objekt je pro všechny záměry a účely v tomto okamžiku neplatný. Některá rozhraní .NET API to zajišťují vyvoláním výjimky ObjectDisposedException, pokud se pokusíte získat přístup k jakýmkoli metodám objektu disposed, například:
var image = UIImage.FromFile ("demo.png");
image.Dispose ();
image.XXX = false; // this at this point is an invalid operation
I když máte stále přístup k proměnné "image", je to opravdu neplatný odkaz a už neukazuje na Objective-C objekt, který je uložený na obrázku.
Ale zrušení objektu v jazyce C# neznamená, že objekt bude nutně zničen. Stačí uvolnit odkaz, který jazyk C# měl k objektu. Je možné, že prostředí Cocoa mohlo vést odkaz na vlastní použití. Pokud například nastavíte vlastnost Image UIImageView na image a pak odstraníte image, základní UIImageView vzal vlastní odkaz a zachová odkaz na tento objekt, dokud ho nedokončíte.
Kdy volat Dispose
Volání Dispose, pokud potřebujete Mono zbavit objektu. Případ použití je v případě, že Mono nemá žádné znalosti, že objekt NSObject ve skutečnosti drží odkaz na důležitý prostředek, jako je paměť nebo fond informací. V takových případech byste měli volat Dispose, aby okamžitě uvolnit odkaz na paměť, místo čekání na Mono provést cyklus uvolňování paměti.
Interně když Mono vytvoří odkazy NSString z řetězců jazyka C#, okamžitě je odstraní, aby snížil množství práce, kterou musí systém uvolňování paměti provést. Čím méně objektů se bude zabývat, tím rychleji se GC spustí.
Kdy zachovat odkazy na objekty
Jedním vedlejším účinkem, který automatická správa paměti má, je, že se GC zbaví nepoužívaných objektů, pokud na ně nejsou žádné odkazy. Což někdy může mít překvapivý vedlejší účinky, například když vytvoříte místní proměnnou, která bude obsahovat kontroler zobrazení nejvyšší úrovně nebo okno nejvyšší úrovně, a pak ty zmizí za zády.
Pokud ve statických proměnných nebo proměnných instance neuchováte odkaz na objekty, Mono na nich bude šťastně volat metodu Dispose() a uvolní odkaz na objekt. Vzhledem k tomu, že to může být jediný nevyřízený odkaz, Objective-C modul runtime za vás zničí objekt.