Swift fra Scratch: Initialisering og initializer Delegasjon
31
Del
4
Del
Dette Cyber Monday Envato Tuts + kurs vil bli redusert til bare $ 3. Ikke gå glipp av
Dette innlegget er en del av en serie som heter Swift fra Scratch.Swift fra Scratch:. Access Control og formues Observatører
I forrige avdrag på Swift fra Scratch, opprettet vi en funksjonell gjøremåls søknad . Datamodellen kunne bruke noen kjærlighet skjønt. I denne opplæringen skal vi refactor datamodellen ved å implementere en tilpasset modell klasse.
1. Data Modell
Datamodellen vi er i ferd med å implementere inkluderer to klasser, en oppgave klasse og en ToDo klasse som arver fra Task klassen. Mens vi lage og implementere disse modell klasser, vi fortsetter vår utforskning av objektorientert programmering i Swift. I denne opplæringen, vi zoomer inn på initialisering av klassen instanser og hvilken rolle arv spiller under initialisering.
Task Class
La oss starte med gjennomføringen av Task klassen. Opprett en ny Swift fil ved å velge New > Fil ... fra Xcode Fil-menyen. Velg Swift fil fra iOS > Kilde delen. Gi filen navnet Task.swift og traff Opprett.
Den grunnleggende gjennomføringen er kort og enkel. Oppgave klassen arver fra NSObject, definert i stiftelsen rammeverk, og har en variabel egenskap navn av typen String. Klassen definerer to initializers, init og init (navn :). Det er noen detaljer som kan reise deg opp så la meg forklare hva som skjer
importere Foundationclass. Oppgave: NSObject {var navn: String bekvemmelighet overstyring init () {self.init (navn: "Ny oppgave")} init ( navn: String) {self.name = navn}}
Fordi init metoden er også definert i NSObject klasse, må vi prefiks initializer med overstyring nøkkelordet. Vi dekket overordnede metoder tidligere i denne serien. I init-metoden, påberope vi init (navn :) metode, passerer i "Ny oppgave" som verdien for navnet parameter.
init (navn :) metoden er en annen initializer, akseptere en enkelt parameter navn av typen String. I denne initializer, blir verdien av den parameter som er tilordnet navnet navnet egenskapen. Dette er lett nok å forstå. Høyre?
Dedikert og Convenience Initializers
Hva er det med det praktiske nøkkelordet prefixing init-metoden? Klassene kan ha to typer initializers, utpekte initializers og dagligvare initializers. Convenience initializers innledes med bekvemmelig søkeord, noe som innebærer at init (navn :) er en utpekt initializer. Hvorfor det? Hva er forskjellen mellom utpekte og convenience initializers?
Eget initializers fullt initial en forekomst av en klasse, noe som betyr at hver eiendom for eksempel har en initiell verdi etter initialisering. Ser på Task klassen, for eksempel, ser vi at navnet eiendommen er satt til verdien av navnet parameter i init (navn :) initializer. Resultatet etter initialisering er et fullt initialisert Task eksempel.
Convenience initializers imidlertid avhengige av en utpekt initializer å skape et fullt initialisert forekomst av klassen. Det er derfor den init initializer av Task klassen påkaller init (navn :) initializer i gjennomføringen. Dette er referert til som initializer delegasjonen. Init initializer delegater initialisering til et utpekt initializer å skape et fullt initialisert forekomst av Task klassen.
Convenience initializers er valgfrie. Ikke hver klasse har en praktisk initializer. Eget initializers er nødvendig og en klasse må ha minst én utpekt initializer å skape et fullt initialisert forekomst av seg selv.
NSCoding Protocol
Gjennomføringen av Task klassen er ikke komplett skjønt. Senere i denne artikkelen vil vi skrive en rekke ToDo tilfeller til disk. Dette er bare mulig hvis forekomster av ToDo klassen kan kodes og dekodes.
Ikke bekymre deg om, dette er ikke rocket science. Vi trenger bare å gjøre oppgaven og ToDo klasser i samsvar med den NSCoding protokollen. Det er derfor Task klassen arver danne NSObject klassen siden NSCoding protokollen kan bare gjennomføres av klasser arve-direkte eller indirekte fra NSObject. Som NSObject klassen, er NSCoding protokoll definert i stiftelsen rammeverket.
Vedta en protokoll er noe vi allerede dekket i denne serien, men det er noen feller som jeg ønsker å påpeke. La oss begynne med å fortelle kompilatoren at Task klassen er i samsvar med NSCoding protokollen
klasse. Oppgave: NSObject, NSCoding {var navn: String ...}
Deretter må vi gjennomføre de to metodene erklært i NSCoding protokollen, init (koder :) og encodeWithCoder (_ :). Gjennomføringen er grei hvis du er kjent med den NSCoding protokollen
importere Foundationclass. Oppgave: NSObject, NSCoding {var navn: Stringobjc nødvendig init (koder aDecoder: NSCoder) {name = aDecoder.decodeObjectForKey ("navn") som! String}objc func encodeWithCoder (aCoder: NSCoder) {aCoder.encodeObject (navn, Forkey: "navn")} bekvemmelighet overstyring init () {self.init (navn: "Ny oppgave")} init (navn: String) { self.name = navn}}
init (koder :) initializer er et utpekt initializer som initialiserer en oppgave eksempel. Selv om vi implementere init (koder :) metode for å samsvare med NSCoding protokollen, vil du aldri trenger å påberope seg denne metoden direkte. Det samme gjelder for encodeWithCoder (_ :), som koder for en forekomst av Task klassen.
Den nødvendige søkeord prefixing init (koder :) metoden viser at hver underklasse av Task klassen må implementere denne metoden . Den nødvendige søkeord bare gjelder initializers, noe som er grunnen til at vi ikke trenger å legge det til encodeWithCoder (_ :) metoden.
Før vi går videre, må vi snakke omobjc attributtet. Fordi NSCoding protokollen er en Objective-C-protokollen, kan protokollen conformance bare kontrolleres ved å legge tilobjc attributtet. I Swift, det finnes ikke noe slikt som protokoll konformitet eller tilleggsprotokoll metoder. Med andre ord, hvis en klasse følger en bestemt protokoll, kompilatoren verifiserer og forventer at hver metode for protokollen er implementert.
ToDo Class
Med Task klasse implementert, er det på tide å implementere ToDo klassen. Opprett en ny Swift fil og navn er ToDo.swift. La oss se på gjennomføringen av ToDo klassen
import Foundationclass ToDo. Task {var gjort: Boolobjc nødvendig init (koder aDecoder: NSCoder) {self.done = aDecoder.decodeObjectForKey ("ferdig") som! Bool super.init (koder: aDecoder)}objc styre func encodeWithCoder (aCoder: NSCoder) {aCoder.encodeObject (gjort, Forkey: "ferdig") super.encodeWithCoder (aCoder)} init (navn: String, gjort: bool) {self.done = gjort super.init (navn: navn)}}
ToDo klassen arver fra Task klasse og erklærer en variabel egenskap gjort av type Bool. I tillegg til de to nødvendige metoder for NSCoding protokollen arver fra Task klassen, erklærer det også en utpekt initializer, init (navn. Gjort :)
Som i Objective-C, refererer super nøkkelord for å superklassen, Oppgave klasse i dette eksemplet. Det er en viktig detalj som fortjener oppmerksomhet. Før du starter init (navn :) metoden på superklassen, hver eiendom erklært av ToDo klassen må klargjøres. Med andre ord, før ToDo klasse delegater initialisering til sin superklasse, hver eiendom ToDo klassen erklærer, må ha en gyldig startverdi. Du kan kontrollere dette ved å bytte rekkefølgen på uttalelser og inspisere feilen som dukker opp.
Det samme gjelder for den init (koder :) metoden. Vi først initialisere gjort eiendommen før påkalle init (koder :) på superklassen. Merk også at vi nedtrykt og kraft pakke resultatet av decodeObjectForKey (_ :) til en Bool bruke som !.
Initializers og Arv
Når du arbeider med arv og initialisering, er det noen regler å huske på. Regelen for utpekte initializers er enkle.
.
Reglene for convenience initializers er litt mer komplisert. Det er to regler å huske på.
.
Med begge modellklassene er implementert, er det på tide å refactor de ViewController og AddItemViewController klasser. La oss starte med sistnevnte.
2. Refactoring AddItemViewController
Trinn 1: Oppdatere
AddItemViewControllerDelegate Protocol
De eneste endringene vi må gjøre i AddItemViewController klassen er relatert til AddItemViewControllerDelegate protokollen. I protokollen erklæringen, endre typen didAddItem fra String til ToDo, modellen klassen vi implementert tidligere
protokollen AddItemViewControllerDelegate {func kontrolleren (controller: AddItemViewController, didAddItem: ToDo)}.
Trinn 2: Oppdatering skape Handling
Dette betyr at vi også må oppdatere skape handling der påberope vi representanten metoden. I den oppdaterte implementering, skaper vi en ToDo eksempel, den sendes til den representanten metoden
IBAction func lage (avsender: AnyObject). {La name = self.textField.text la element = ToDo (navn: navn, gjort: false) hvis la delegat = self.delegate {delegate.controller (selv, didAddItem: element)}}
3. Refactoring ViewController
Trinn 1: Oppdatere elementer Eiendom
ViewController klassen krever litt mer arbeid. Må vi først endre typen av elementene eiendommen til [ToDo], en rekke ToDo tilfeller
Var poster:. [ToDo] = [] {didSet {la hasItems = items.count > 0 self.tableView.hidden = hasItems self.messageLabel.hidden = hasItems}}
Trinn 2: Tabell Se datakilde Metoder
Dette betyr også at vi må refactor noen andre metoder, som for eksempel den cellForRowAtIndexPath (_ :) metoden vist nedenfor. Fordi elementer matrisen inneholder nå ToDo tilfeller sjekke om et element er markert som gjøres er mye enklere. Vi bruker Swifts trefoldig betinget operatøren å oppdatere tabellen visning cellens tilbehørstype
func Tableview (Tableview: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath.) - ≫ UITableViewCell {//Hente varen la element = self.items [indexPath.row] //dequeue Tabell Cell la tableViewCell = tableView.dequeueReusableCellWithIdentifier ("TableViewCell", forIndexPath: indexPath) som! UITableViewCell //Konfigurer Tabell Cell tableViewCell.textLabel? .text = Item.name tableViewCell.accessoryType = item.done? .Checkmark: .None Tilbake tableViewCell}
Når brukeren sletter et element, vi trenger bare å oppdatere elementer eiendom ved å fjerne den tilsvarende ToDo eksempel. Dette gjenspeiles i gjennomføringen av Tableview (_: commitEditingStyle. ForRowAtIndexPath :) metoden vist nedenfor
func Tableview (Tableview: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {if editingStyle == .Delete {//Fetch Sak la element = self.items [indexPath.row] //Oppdater Elementer self.items.removeAtIndex (indexPath.row) //Save State self.saveItems () //Update Tabell tableView.deleteRowsAtIndexPaths ([indexPath] withRowAnimation: UITableViewRowAnimation.Right)}}
Trinn 3: Se tabell Delegat Metoder
Oppdatering av tilstanden til et element når brukeren kraner rad håndteres i Tableview (_: didSelectRowAtIndexPath :) metode. Gjennomføringen av denne UITableViewDelegate metoden er mye enklere takket være ToDo klassen
func Tableview. (Tableview: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {tableView.deselectRowAtIndexPath (indexPath, animert: true) //Fetch Element la element = selvtillit. elementer [indexPath.row] //Fetch Tabell Cell la tableViewCell = tableView.cellForRowAtIndexPath (indexPath) //Update Element item.done =! item.done //Update Tabell Cell tableViewCell? .accessoryType = item.done? .Checkmark: .None //Save State self.saveItems ()}
Den tilsvarende ToDo eksempel er oppdatert og denne endringen gjenspeiles av utsikten bordet. Å spare staten, påberope vi saveItems stedet for saveCheckedItems
Trinn 4:. Legg til element View Controller Delegat Metoder
Fordi vi oppdatert AddItemViewControllerDelegate protokollen, trenger vi også å oppdatere ViewController implementering av denne protokoll. Forandringen er imidlertid enkel. Vi trenger bare å oppdatere metoden signaturen
func kontrolleren. (Controller: AddItemViewController, didAddItem: ToDo) {//Oppdater datakilde self.items.append (didAddItem) //Lagre Statlige self.saveItems () //Reload Table Vis self.tableView.reloadData () //Avvis Legg til element View Controller self.dismissViewControllerAnimated (sant, ferdigstillelse: null)}
Trinn 5: Saving Elementer
pathForItems
I stedet for å lagre elementene i brukerstandarder database, kommer vi til å lagre dem i programmets dokumenter katalogen. Før vi oppdatere loadItems og saveItems metoder, vi kommer til å gjennomføre en hjelper metode kalt pathForItems. Metoden er privat og returnerer en bane, plasseringen av elementene i dokumenter katalogen
privat funk pathForItems () - >.; String {la documentsDirectory = NSSearchPathForDirectoriesInDomains (.DocumentDirectory, .UserDomainMask, true) .first som! String returnere documentsDirectory.stringByAppendingPathComponent ("items")}
Vi først hente banen til dokumenter katalogen i programmets sandkasse ved å påberope NSSearchPathForDirectoriesInDomains (_: _: _ :). Fordi denne metoden returnerer en matrise av strenger, vi ta det første elementet, kraft pakke den, og nedbrutte den til en String. Verdien vi kommer tilbake fra pathForItems er sammensatt av banen til dokumenter katalog med strengen "elementer" legges til det.
loadItems
loadItems metoden endrer ganske mye. Vi først lagre resultatet av pathForItems i en konstant heter bane. Vi dearkiverer objektet arkivert på den veien og nedbrutte det til en valgfri rekke ToDo tilfeller. Vi bruker valgfri binding å pakke den valgfrie og tilordne den til en konstant navngitte elementer. I hvis klausulen, vi tilordner verdien som er lagret i elementer i elementer eiendommen.
Private funk loadItems () {la path = self.pathForItems () hvis la elementer = NSKeyedUnarchiver.unarchiveObjectWithFile (bane) som? [ToDo] {self.items = elementer}}
saveItems
saveItems metoden er kort og enkel. Vi lagrer resultatet av pathForItems i en konstant heter bane og påberope archiveRootObject (_: toFile :) På NSKeyedArchiver, passerer i elementer eiendom og bane. Vi trykker resultatet av operasjonen til konsollen
private funk saveItems () {la path = self.pathForItems () hvis NSKeyedArchiver.archiveRootObject (self.items, toFile: bane). {Println ("lagret")} else {println ("Saving Failed")}}
Trinn 6: Rydder opp
La oss slutte med den morsomme delen, slette koden. Start med å fjerne checkedItems eiendom på toppen siden vi ikke lenger trenger det. Som et resultat, kan vi også fjerne loadCheckedItems og saveCheckedItems metoder, og alle referanser til disse metodene i ViewController klassen.
Bygg og kjøre programmet for å se om alt er fremdeles arbeider. Datamodellen gjør applikasjonen kode mye enklere og pålitelig. Takket være ToDo klassen, administrerende elementene i vår liste mye er nå enklere og mindre utsatt for feil.
Konklusjon
I denne opplæringen, vi refactored datamodellen av vår søknad. Du har lært mer om objektorientert programmering og arv. Instance initialisering er et viktig begrep i Swift så sørg for at du forstår hva vi har dekket i denne opplæringen. Du kan lese mer om initialisering og delegering initializer i The Swift Programming Language.