En introduksjon til Swift Del 2

I den første artikkelen i denne introduksjonsserien om Swift snakket vi om Swifts filosofi, tok en første titt på sin syntaks og fremhevet noen viktige forskjeller med Objective-C. I denne artikkelen fortsetter vi utforskningen av Swifts syntaks. Du vil også lære om optionsals og se hvordan minnehåndtering fungerer i Swift.

1. Conditionals og Loops

Hvis

Hvis setningene er like i Swift og Objective-C, med unntak av to subtile forskjeller:

  • parenteser rundt tilstandsvariabelen er valgfrie
  • krøllete braces er påkrevd

Disse handler om de eneste forskjellene med om setninger i Mål-C.

Ranges

Som vi så i den første artikkelen, inkluderer Swift to rekkevidde operatører ... < og ... å angi en rekke verdier. Disse to operatørene er Half-closed Range Operator og lukket operatør.

Et halvt lukket område, for eksempel 1… <5, representerer verdiene 1, 2, 3 og 4, unntatt 5. Et lukket område, for eksempel 1 ... 5, representerer verdiene 1, 2, 3, 4 og inkluderer 5.

Rangene kan brukes i til løkker, matrise abonnement, og til og med i bytte om uttalelser. Ta en titt på følgende eksempler.

// for loop eksempel for jeg i 1 ... <10   // iterates from 1 to 9
// array subscript eksempel la noenArray = ["apple", "pair", "fersken", "vannmelon", "jordbær"] for frukt i someArray [2 ... <4]  println(fruit)  // outputs: peach and watermelon
// bytt eksempelbryter noenInt tilfelle 0: // gjør noe med 0 tilfelle 1 ... <5: // do something with 1,2,3,4 case 5… 10: // do something with 5,6,7,8,9,10 default: // everything else 

Bytte om

Switch-setninger er kraftigere i Swift enn de er i Objective-C. I mål-C må resultatet av uttrykket av en bryteretning være av typen heltall, og verdiene for hver tilfelleoppgave skal være et konstant eller konstant uttrykk. Dette er ikke sant i Swift. I Swift kan saksoppgavene være av noe slag, inkludert intervaller.

I Swift har bryteren nr gå i stykker uttalelser og det faller ikke automatisk fra ett tilfelle til et annet. Når du skriver en bryteretning, må du være oppmerksom på at alle forhold håndteres av saksoppgavene, hvis du ikke gjør det, vil det føre til en kompilatorfeil. En sikker måte å dekke alle forhold på er å inkludere a misligholde saksoppgave.

Her er et eksempel på a bytte om uttalelse med string saker:

la grønnsak = "rød pepper" bryter grønnsak sak "selleri": la vegetabilskComment = "Legg til noen rosiner og lage maur på en logg." tilfelle "agurk", "watercress": la vegetabilskComment = "Det ville lage en god te sandwich." standard: la vegetableComment = "Alt smaker godt i suppe."  

I Swift faller ikke saksdeklarasjoner som standard. Dette var en bevisst designbeslutning for å unngå vanlige feil. Hvis et bestemt tilfelle trenger å falle gjennom, kan du bruke faller gjennom søkeord for å indikere dette til kompilatoren.

bytt noenInt case 0: // gjør noe med 0 tilfelle 1: // gjør noe med 1 tilfelle 2: // gjør noe med 2 fallthrough standard: // gjør noe for alt annet // tilfelle 2 vil falle gjennom til standard sak

Det stopper ikke her. Swift legger til to andre funksjoner for å bytte, verdi bindinger og hvor klausul. Verdibinding brukes med tilfelle la søkeord for å binde en konstant med matchende tilfelle. Var-klausulen legger til en ekstra betingelse for saksoppstillingen ved hjelp av hvor søkeord.

Disse to konseptene er bedre forklart med eksempler. Følgende kodeblokk viser hvordan verdi bindende virker.

la noenPoint = (xaxis: 2, yaxis: 0) bytte somePoint tilfelle (la x, 0): println ("på x-aksen med en x-verdi på \ (x)") tilfelle (0, la y) println ("på y-aksen med ay verdi av \ (y)") tilfelle la (x, y): println ("et annet sted på (\ (x), \ (y))")

Den første saken uttalelse, tilfelle (la x, 0), vil matche verdiene der y-aksen er lik 0 og noen verdi for x-aksen, og vi binder x-aksen til konstanten x å bli brukt i saken setningen.

Her er et eksempel på hvor klausulen er i aktion.

la grønnsak = "rød pepper" bytt vegetabilsk tilfelle "selleri": println ("Legg til noen rosiner og lage maur på en logg.") tilfelle la x hvor x.hasSuffix ("pepper"): println ("Jeg er allergisk til \ (x) ") standard: println (" Alt smaker godt i suppe. ") // utganger: Jeg er allergisk mot rød pepper

2. Funksjoner og lukkinger

funksjoner

Definisjon av funksjoner og nedleggelser er enkelt i Swift. Mål-C-utviklere kjenner dem bedre som funksjoner og blokker.

I Swift kan funksjonsparametere ha standardverdier, noe som minner om skriptspråk, som PHP og Ruby.

Syntaxen for funksjoner er som følger:

func-funksjonnavn (parameternavn: Type = DefaultValue) -> returnType [...] returnertype; 

Å skrive en si hei funksjon som tar en parameter Navn av type string og returnerer a bool når vellykket skriver vi følgende:

func sayHello (navn: String) -> Bool println ("hei \ (navn)"); returnere sant;  sayHello ("World") // utgang // hallo verden

For å passere en standardverdi for Navn parameteren, vil funksjonens implementering se ut som følger:

func sayHello (navn: String = "World") -> Bool println ("hei \ (navn)"); returnere sant;  sayHello () // output // hei World sayHello ("Mike") // output // hei mike

En funksjon som er helt fraværende i Objective-C er tupler. I Swift kan funksjoner returnere flere verdier i form av en tuple. Tuples blir behandlet som en enkelt variabel, noe som betyr at du kan passere den rundt akkurat som en variabel.

Tuples er veldig enkle å bruke. Faktisk har vi allerede jobbet med tuples i forrige artikkel da vi oppnådde en ordbok. I neste kodestykke er nøkkel / verdi-paret en tuple.

for (nøkkel, verdi) i noenDictionary println ("Key \ (key) har verdi \ (verdi)"

Hvordan brukes tuples og hvordan drar du nytte av dem? La oss ta en titt på et annet eksempel. La oss endre det ovenfor si hei funksjon for å returnere en boolesk når vellykket, så vel som den resulterende meldingen. Vi gjør dette ved å returnere en tuple, (Bool, String). Den oppdaterte si hei funksjonen ser slik ut:

func sayHello (navn: String = "World") -> (Bool, String) let greeting = "hei \ (navn)" returnere (sann, hilsen);  la (vellykket hilsen) = sayHello () println ("sayHello returnerte suksess: \ (suksess) med hilsen: \ (hilsen)"); // utgang // sayHello returnerte suksess: 1 med hilsen: hallo verden 

Tuples har vært på ønskeliste av mange Objective-C programmerere i lang tid.

En annen kul funksjon av tuples er at vi kan nevne de returnerte variablene. Hvis vi går tilbake til forrige eksempel og gir navn til variablene i tupelen, får vi følgende:

func sayHello (navn: String = "World") -> (suksess: Bool, hilsen: String) let greeting = "hei \ (navn)" returnere (sann, hilsen);  la status = sayHello () println ("sayHello returnerte suksess: \ (status.success) med hilsen: \ (status.greeting)"); // utgang // sayHello returnerte suksess: 1 med hilsen: hallo verden 

Dette betyr at i stedet for å definere en separat konstant for hvert returelement av en tuple, kan vi få tilgang til de returnerte tuple-elementene ved hjelp av punktnotasjon som vist i eksemplet ovenfor, status.success og status.greeting.

nedleggelser

Lukker i Swift er de samme som blokkene i Objective-C. De kan defineres inline, bestått som en parameter, eller returneres av funksjoner. Vi bruker dem akkurat som vi ville bruke blokker i Objective-C.

Det er også enkelt å definere nedleggelser. Faktisk er en funksjon et spesielt tilfelle av nedleggelser. Så det er ikke rart at definere en lukking ser mye ut som å definere en funksjon.

Lukk er en førsteklasses type, som betyr at de kan bestås og returneres av funksjoner som alle andre typer, for eksempel intstringbool, etc. Lukker er i hovedsak kodeblokker som kan kalles senere og har tilgang til omfanget der de ble definert.

Å lage en navnløs lukning er like enkelt som å pakke inn en blokk med koden i krøllete braces. Parametrene og returtypen til lukkingen er skilt fra lukkens kropp med i søkeord.

La oss si at vi ønsker å definere et lukket som kommer tilbake ekte hvis et tall er jevnt, så kan lukkingen se ut som:

la isEven = (nummer: Int) -> Bool i let mod = nummer% 2 retur (mod == 0)

De ISEVEN lukking tar en int som sin enparameter og returnerer a bool. Typen av denne lukkingen er (nummer: Int) -> Bool, eller (Int -> Bool) for kort. Vi kan ringe ISEVEN hvor som helst i vår kode akkurat som vi ville påkalle en kodeblokk i Objective-C.

For å passere en lukking av denne typen som en parameter av en funksjon, bruker vi lukkets type i funksjonens definisjon:

la isEven = (nummer: Int) -> Bool i let mod = tall% 2; returnere (mod == 0);  func verifyIfEven (nummer: Int, verifier: (Int-> Bool)) -> Bool return verifier (nummer);  verifyIfEven (12, erEven); // returnerer sant verifiseringIfEven (19, erEven); // returnerer false

I eksempelet ovenfor er det bekreft parameteren til verifyIfEven funksjon er et lukning som vi overfører til funksjonen.

3. Klasser og strukturer

klasser

Det er på tide å snakke om hjørnestenen i objektorientert programmering, klasser. Klasser, som nevnt tidligere, er definert i en enkelt implementeringsfil med en .fort forlengelse. Eiendomserklæringer og metoder er alle definert i den filen.

Vi lager en klasse med klasse søkeord etterfulgt av klassens navn. Klassens implementering er innpakket i et par krøllete braces. Som i mål-C, er navngivningskonvensjonen for klasser å bruke øvre kamelveske for klassenavn.

klasse hotell // egenskaper // funksjoner

Å opprette en forekomst av Hotell klasse vi skriver:

la h = Hotel ()

I Swift er det ikke nødvendig å ringe i det på objekter som i det kalles automatisk for oss.

Klassen arv følger det samme mønsteret som i Mål-C, et kolon skiller klassenavnet og det som er dets superklasse. I følgende eksempel, Hotell arv fra BigHotel klasse.

klassen BigHotel: Hotel 

Som i mål-C bruker vi punktnotasjon for å få tilgang til objektets egenskaper. Imidlertid bruker Swift også punktnotasjonen til å påkalle klasse- og instansmetoder som du kan se nedenfor.

// Objektiv-C UIView * view = [[UIView alloc] init]; [self.view addSubview: view]; // Swift la vise = UIView () self.view.addSubview (se)

Eiendommer

En annen forskjell med Objective-C er at Swift ikke skiller mellom instansvariabler (ivars) og egenskaper. En instansvariabel er en eiendom.

Deklarere en egenskap er akkurat som å definere en variabel eller en konstant, ved hjelp av Var og la søkeord. Den eneste forskjellen er konteksten der de er definert, det vil si konteksten til en klasse.

klasse hotell la rom = 10 var fullrom = 0

I eksemplet ovenfor, rom er en uforanderlig verdi, en konstant, satt til 10 og fullRooms er en variabel med en startverdi på 0, som vi kan endre senere. Regelen er at egenskaper må initialiseres når de er erklært. Det eneste unntaket til denne regelen er valgmuligheter, som vi diskuterer om et øyeblikk.

Beregnede egenskaper

Swift-språket definerer også beregnede egenskaper. Beregnede egenskaper er ikke noe annet enn fancy getters og setters som ikke lagrer en verdi. Som navnet indikerer, blir de beregnet eller evaluert på fluen.

Nedenfor er et eksempel på en beregnet eiendom. Jeg har endret rom eiendom til a Var for resten av disse eksemplene. Du vil finne ut hvorfor senere.

Klasse Hotel var rom = 10 var fullRooms = 0 var beskrivelse: String get return Hotellstørrelse: \ (rom) rom kapasitet: \ (fullRooms) / \ (rom) "

Fordi det beskrivelse Eiendommen er skrivebeskyttet og har bare a komme tilbake uttalelse, kan vi utelate søkeord og krøllete braces, og bare hold komme tilbake uttalelse. Dette er shorthand, og det er det jeg skal bruke i resten av denne opplæringen.

Klasse Hotel var rom = 10 var fullRom = 0 var beskrivelse: String retur "Hotellstørrelse: \ (rom) rom kapasitet: \ (fullrom) / \ (rom)"

Vi kan også definere lese-skrive beregnede egenskaper. I vår klasse Hotell, vi vil ha en emptyRooms eiendom som får antall tomme rom på hotellet, men vi vil også oppdatere fullRooms når vi setter emptyRooms beregnet eiendom. Vi kan gjøre dette ved å bruke sett søkeord som vist nedenfor.

Klasse Hotel var rom = 10 var fullRooms = 0 var beskrivelse: String retur "Hotellstørrelse: \ (rom) rom kapasitet: \ (fullRooms) / \ (rom)" var tomRommene: Int get return rooms - fullRooms set // newValue konstant er tilgjengelig her // som inneholder bestått verdi hvis (newValue < rooms)  fullRooms = rooms - newValue  else  fullRooms = rooms     let h = Hotel() h.emptyRooms = 3 h.description // Size of Hotel: 10 rooms capacity:7/10

I emptyRooms setter, den nyVerdi Konstant er overlevert til oss og representerer verdien som er overført til setteren. Det er også viktig å merke seg at beregnede egenskaper er alltid deklarert som variabler, ved hjelp av Var søkeord, fordi deres beregnede verdi kan endres.

metoder

Vi har allerede dekket funksjoner tidligere i denne artikkelen. Metoder er ikke mer enn funksjoner som er knyttet til en type, for eksempel en klasse. I følgende eksempel implementerer vi en instansmetode, bookNumberOfRooms, i Hotell klasse vi opprettet tidligere.

Klasse Hotel var rom = 10 var fullRooms = 0 var beskrivelse: String retur "Hotellstørrelse: \ (rom) rom kapasitet: \ (fullRooms) / \ (rom)" var tomRommene: Int get return rooms - fullRooms set // newValue konstant er tilgjengelig her // som inneholder bestått verdi hvis (newValue < rooms)  fullRooms = rooms - newValue  else  fullRooms = rooms    func bookNumberOfRooms(room:Int = 1) -> Bool if (self.emptyRooms> rom) self.fullRooms ++; returnere true ellers return false la h = Hotel () h.emptyRooms = 7 h.description // Hotellstørrelse: 10 rom kapasitet: 3/10 h.bookNumberOfRooms (rom: 2) // returnerer sann h .description // Hotellstørrelse: 10 rom kapasitet: 5/10 h.bookNumberOfRoom () // returnerer sann h.description // Hotellstørrelse: 10 rom kapasitet: 6/10 

Initializers

Standardinitialiseringen for klasser er i det. I i det funksjon, setter vi innledningsverdiene til forekomsten som er opprettet.

For eksempel, hvis vi trenger en Hotell subclass med 100 rom, så ville vi trenge en initializer å sette rom eiendom til 100. Husk at jeg tidligere har endret rom fra å være en konstant til å være en variabel i Hotell klasse. Årsaken er at vi ikke kan endre arvelige konstanter i en underklasse, bare arvelige variabler kan endres.

klasse BigHotel: Hotel init () super.init () rooms = 100 la bh = BigHotel () println (bh.description); // Størrelse på hotellet: 100 rom kapasitet: 0/100

Initialisatorer kan også ta parametere. Følgende eksempel viser hvordan dette virker.

klasse CustomHotel: Hotel init (størrelse: Int) super.init () rooms = size la c = CustomHotel (størrelse: 20) c.description // Hotellstørrelse: 20 rom kapasitet: 0/20

Overordnede metoder og beregnede egenskaper

Dette er en av de kuleste tingene i Swift. I Swift kan en underklasse overstyre begge metoder og beregnede egenskaper. For å gjøre det bruker vi overstyring søkeord. La oss overstyre beskrivelse Beregnet eiendom i CustomHotel klasse:

klasse CustomHotel: Hotel init (størrelse: Int) super.init () rooms = size overstyre var beskrivelse: String return super.description + "Howdy!"  la c = CustomHotel (størrelse: 20) c.description // Størrelse på hotellet: 20 rom kapasitet: 0/20 Howdy! 

Resultatet er det beskrivelse returnerer resultatet av superklassens beskrivelse metode med strengen "Howdy!" lagt til det.

Hva er kult om overordnede metoder og beregnede egenskaper er overstyring søkeord. Når kompilatoren ser overstyring søkeord, det kontrollerer hvor klassens superklasse implementerer metoden som blir overstyrt. Kompilatoren kontrollerer også om egenskapene og metodene til en klasse er i konflikt med egenskaper eller metoder høyere opp i arvstreet.

Jeg vet ikke hvor mange ganger en skrivefeil i en overstyrt metode i Objective-C gjorde meg forbanne fordi koden ikke fungerte. I Swift vil kompilatoren fortelle deg nøyaktig hva som er galt i disse situasjonene.

strukturer

Strukturer, definert med struct søkeord, er kraftigere i Swift enn de er i C og Objective-C. I C definerer strukturer bare verdier og pekere. Swift structs er akkurat som C-strukturer, men de støtter også beregnede egenskaper og metoder.

Alt du kan gjøre med en klasse, kan du gjøre med en struktur, med to viktige forskjeller:

  • strukturer støtter ikke arv som klasser gjør
  • strukturer blir videresendt av verdi mens klasser er bestått ved referanse

Her er noen få eksempler på strukturer i Swift:

struktur Rect var opprinnelse: Punkt var størrelse: Størrelse var område: Dobbel return size.width * size.height func erBiggerThanRect (r: Rect) -> Bool return (self.area> r.area) struct Point var x = 0 var y = 0 struct Størrelse var bredde = 0 var høyde = 0

4. Valgmuligheter

Løsning på et problem

Alternativer er et nytt konsept hvis du kommer fra Objective-C. De løser et problem vi alle møter som programmerere. Når vi får tilgang til en variabel hvis verdi vi ikke er sikre på, returnerer vi vanligvis en indikator, kjent som en sentinel, for å indikere at den returnerte verdien er en ikke-verdi. La meg illustrere dette med et eksempel fra Mål-C:

NSString * someString = @ "ABCDEF"; NSInteger pos = [someString rangeOfString: @ "B"]. Sted; // pos = 1

I eksemplet ovenfor prøver vi å finne posisjonen til @ "B" i someString. Hvis @ "B" er funnet, er plasseringen eller posisjonen lagret i pos. Men hva skjer hvis @ "B" er ikke funnet i someString?

Dokumentasjonen sier at rangeOfString: returnerer en NSRange med plassering satt til NSNotFound konstant. I tilfelle av rangeOfString:, Sentinel er NSNotFound. Sentineller brukes til å indikere at den returnerte verdien ikke er gyldig.

I kakao er det mange bruksområder av dette konseptet, men sentinelverdien skiller seg fra kontekst til kontekst, 0, -1, NULL, NSIntegerMax, INT_MAX, Nil, etc. Problemet med programmereren er at hun må huske hvilken sentinel som brukes i hvilken sammenheng. Hvis programmereren ikke er forsiktig, kan hun feile en gyldig verdi for en sentinel og omvendt. Swift løser dette problemet med valgmuligheter. Å sitere Brian Lanier "Valgmuligheter er den ene sentinel til å herske dem alle."

Valgmuligheter har to stater, a nil state, som betyr at valgfri inneholder ingen verdi, og en annen stat, som betyr at den har en gyldig verdi. Tenk på alternativer som en pakke med en indikator for å fortelle om innholdet i pakken er gyldig eller ikke.

bruk

Alle typer i Swift kan bli en valgfri. Vi definerer en valgfri ved å legge til en ? etter typedeklarasjonen slik:

la noenInt: Int? // noenInt == nil

Vi tilordner en verdi til en valgfri pakke, akkurat som vi gjør med konstanter og variabler.

noenInt = 10 // littInt! == 10

Husk at alternativene er som pakker. Da vi erklærte la noenInt: Int?, Vi definerte en tom boks med en verdi på nil. Ved å tildele verdien 10 til valgfritt inneholder boksen et heltall som er lik 10 og dens indikator eller tilstand blir ikke null.

For å komme til innholdet i en valgfri bruker vi ! operatør. Vi må være sikre på at tilleggsutstyret har en gyldig verdi før den pakkes ut. Hvis du ikke gjør det, vil det oppstå en kjøretidsfeil. Slik får vi tilgang til verdien som er lagret i en valgfri:

if (someInt! = nil) println ("someInt: \ (someInt!)") else println ("noen har ingen verdi") // noenInt: 10

Ovennevnte mønster er så vanlig i Swift at vi kan forenkle kodeboksen ovenfor ved å bruke valgfri binding med hvis la søkeord. Ta en titt på den oppdaterte kodeblokken nedenfor.

hvis la verdi = noenInt println ("someInt: \ (verdi)" else println ("noenInt har ingen verdi")

Alternativer er den eneste typen som kan ta en nil verdi. Konstanter og variabler kan ikke initialiseres eller settes til nil. Dette er en del av Swifts sikkerhetspolitikk, alle ikke-valgfrie variabler og konstanter ha en verdi.

5. Minnehåndtering

Hvis du husker, tilbake da ARC ble introdusert, brukte vi sterk og svak søkeord for å definere objekt eierskap. Swift har også a sterk og svak eierskapsmodell, men det introduserer også en ny, uten tilsyn. La oss ta en titt på hver objekt eierskapsmodell i Swift.

sterk

Sterke referanser er standard i Swift. Mesteparten av tiden eier vi objektet som vi refererer til, og vi er de som er ansvarlige for å holde det refererte objektet levende.

Siden sterke referanser er standard, er det ikke nødvendig å eksplisitt holde en sterk referanse til et objekt, noe referanse er en sterk referanse.

svak

En svak referanse i Swift indikerer at referansen peker mot et objekt som vi ikke er ansvarlige for å holde oss i live. Det brukes hovedsakelig mellom to objekter som ikke trenger den andre til å være rundt for at objektet skal fortsette sin livssyklus.

Det er en men, derimot. I Swift må svake referanser alltid være variabler med en valgfri type, fordi de er satt til nil når det refererte objektet er allokert. De svak Søkeord brukes til å erklære en variabel som svak:

Svak Var Vis: UIView?

uten tilsyn

Ukjente referanser er nye for Objective-C programmerere. En ukjent referanse betyr at vi ikke er ansvarlige for å holde det refererte objektet levende, akkurat som svake referanser.

Forskjellen med en svak referanse er at en ukjent referanse ikke er satt til nil når objektet det refererer til, er deallokert. En annen viktig forskjell med svake referanser er at ukjente referanser er definert som en ikke-valgfri type.

Ukjente referanser kan være konstanter. Et ukjent objekt eksisterer ikke uten sin eier og derfor er den ukjente referansen aldri nil. Ukjente referanser trenger uten tilsyn søkeord før definisjonen av variabelen eller konstanten.

Ukjent var visning: UIView

Konklusjon

Swift er et fantastisk språk som har mye dybde og potensial. Det er morsomt å skrive programmer med, og det fjerner mye av boilerplate-koden vi skriver i Objective-C for å sikre at vår kode er trygg.

Jeg anbefaler The Swift Programming Language, som er tilgjengelig gratis i Apples iBooks Store.