O introducere în Swift Partea 2

În primul articol al acestei serii introductive despre Swift, am vorbit despre filozofia lui Swift, am aruncat o primă privire asupra sintaxei sale și am subliniat câteva diferențe cheie cu Obiectiv-C. În acest articol, continuăm să explorăm sintaxa lui Swift. De asemenea, veți afla despre opțiunile opționale și veți vedea cum funcționează gestionarea memoriei în Swift.

1. Condiții și bucle

Dacă

Dacă declarațiile sunt identice în Swift și Obiectiv-C, cu excepția a două diferențe subtile:

  • parantezele din jurul variabilei condiției sunt opționale
  • sunt necesare acolade

Acestea sunt despre singurele diferențe cu declarațiile din Obiectiv-C.

Variază

După cum am văzut în primul articol, Swift include doi operatori de gamă ... < și ... pentru a specifica o serie de valori. Acești doi operatori sunt operatorul de interval semi-închis si operatorul cu interval închis.

O gamă pe jumătate închisă, cum ar fi 1 ... <5, reprezintă valorile 1, 2, 3 și 4, cu excepția 5. Un interval închis, cum ar fi 1 ... 5, reprezintă valorile 1, 2, 3, 4 și include 5.

Pot fi folosite canale în pentru bucle, mulțime subscript, și chiar în intrerupator declarații. Consultați următoarele exemple.

// pentru exemplul buclă pentru i în 1 ... <10   // iterates from 1 to 9
// exemplu subscript array lăsați someArray = ["măr", "pereche", "piersic", "pepene verde", "căpșuni"] pentru fructe în uneleArray [2 ... <4]  println(fruit)  // outputs: peach and watermelon
// comutare exemplu switch someInt caz 0: // face ceva cu 0 caz 1 ... <5: // do something with 1,2,3,4 case 5… 10: // do something with 5,6,7,8,9,10 default: // everything else 

Intrerupator

Comenzile de comutare sunt mai puternice în Swift decât în ​​Obiectiv-C. În Obiectiv-C, rezultatul expresiei unei instrucțiuni switch trebuie să fie de tip integer și valorile fiecărei instrucțiuni de caz trebuie să fie o expresie constantă sau constantă. Acest lucru nu este valabil în Swift. În Swift, declarațiile de caz pot fi de orice tip, inclusiv intervalele.

În Swift, comutatorul nu are pauză declarații și nu se încadrează în mod automat de la un caz la altul. Când scrieți o instrucțiune de comutator, trebuie să aveți grijă ca toate condițiile să fie tratate de instrucțiunile sale de caz, dacă nu faceți acest lucru va duce la o eroare de compilator. O modalitate sigură de a acoperi toate condițiile este aceea de a include a Mod implicit declarație de caz.

Iată un exemplu de a intrerupator declarație cu Şir cazuri:

lăsați legumele = "piper roșu" comută legume caz "țelină": lăsați legumeleComment = "Adăugați niște stafide și faceți furnici pe un jurnal". caz "castravete", "Nasture": lăsați legumeleComment = "Asta ar face un sandwich de ceai bun". implicit: lăsați legumeleComment = "Totul are un gust bun în supă".  

În declarațiile Swift, declarațiile de caz nu cad în mod implicit. Aceasta a fost o decizie deliberată de proiectare pentru a evita erorile comune. Dacă un caz specific trebuie să cadă, puteți folosi cadea prin cuvinte cheie pentru a indica acest lucru compilatorului.

comutați cevaIntre caz 0: // face ceva cu 0 caz 1: // face ceva cu 1 caz 2: // face ceva cu 2 implicit fallthrough: // face ceva pentru orice altceva // // caz 2 va cădea la implicit caz

Nu se oprește aici. Swift adaugă alte două funcții pentru comutare, legăturile de valoare si Unde clauză. Valoarea de legare este folosită cu caz cuvinte cheie pentru a lega o constantă cu cazul de potrivire. Clauza unde adaugă o condiție suplimentară instrucțiunii caz folosind Unde cuvinte cheie.

Aceste două concepte sunt mai bine explicate prin exemple. Următorul bloc de coduri arată cum valoare obligatorie lucrări.

(x, 0, y): println ("pe axa x cu o valoare x a lui \ (x)") (0, let y): println ("pe axa y cu valoarea ay a lui \ (y)") let (x, y): println ("undeva la (\ (x), \ (y)

Prima declarație de caz, caz (x, 0), se vor potrivi cu valorile în care se află yaxis este egal cu 0 și orice valoare pentru xaxis, și ne legăm xaxis la constanta X pentru a fi utilizate în declarația de caz.

Iată un exemplu de clauză unde se află în acțiune.

lăsați legumele = "piper roșu" comutați legume caz "țelină": println ("Adăugați niște stafide și faceți furnici pe un jurnal. la \ (x) ") implicit: println (" Totul are un gust bun în supă ") // ieșiri: Sunt alergic la piper roșu

2. Funcții și închideri

funcţii

Definirea funcțiilor și a închiderilor este ușoară în Swift. Dezvoltatorii obiectiv-C le cunosc mai bine ca funcții și blocuri.

În Swift, parametrii funcției pot avea valori implicite, care amintește de limbile de scripting, cum ar fi PHP și Ruby.

Sintaxa pentru funcții este după cum urmează:

func functionName (parametrulName: Type = DefaultValue) -> returnType [...] retur returnType; 

Pentru a scrie a spune buna care are un parametru Nume de tip Şir și returnează a bool când reușim, scriem următoarele:

func sayHello (nume: String) -> Bool println ("hello \ (name)"); return true;  sayHello ("Lumea") // ieșire // salut Lumea

Pentru a trece o valoare implicită pentru Nume parametru, implementarea funcției ar arăta astfel:

func sayHello (nume: String = "Lumea") -> Bool println ("hello \ (name)"); return true;  sayHello () // ieșire // hello World sayHello ("mike") // output // hello mike

O caracteristică care este complet absentă în Obiectiv-C este tuple. În funcția Swift, funcțiile pot returna mai multe valori sub formă de tuple. Tupele sunt tratate ca o singură variabilă, ceea ce înseamnă că le poți trece în jurul lor ca o variabilă.

Tupele sunt foarte usor de folosit. De fapt, am lucrat deja cu tupluri în articolul precedent când am enumerat un dicționar. În următorul fragment de cod, perechea de chei / valoare este o nucă.

pentru (cheie, valoare) în uneleDictionary println ("Cheia \ (cheie) are valoarea \ (valoare)"

Cum sunt utilizate tuplurile și cum beneficiezi de acestea? Să aruncăm o privire la un alt exemplu. Să modificăm cele de mai sus spune buna pentru a returna un boolean atunci când a reușit, precum și mesajul rezultat. Facem asta prin returnarea unei tuple, (Bool, String). Actualizată spune buna funcționează astfel:

func sayHello (nume: String = "Lumea") -> (Bool, String) să salute = "hello \ (name)" retur (adevărat, salut);  let (succes, salut) = sayHello () println ("sayHello a returnat succesul: \ (succes) cu salut: \ (salut)"); // output // sayHello a revenit succes: 1 cu salut: salut lume 

Tupele au fost pe lista de dorințe ale multor programatori Obiectiv-C pentru o lungă perioadă de timp.

O altă caracteristică interesantă a tuplurilor este că putem numi variabilele returnate. Dacă vom revedea exemplul anterior și vom da nume variabilelor tuplei, obținem următoarele:

func sayHello (nume: String = "Lumea") -> (succes: Bool, salut: String) să salute = "hello \ (name)" return (true, greeting);  let status = sayHello () println ("sayHello a returnat succesul: \ (status.success) cu salut: \ (status.greeting)"); // output // sayHello a revenit succes: 1 cu salut: salut lume 

Acest lucru înseamnă că, în loc de a defini o constantă separată pentru fiecare element de returnare al unei tuple, putem accesa elementele de trupă returnate folosind notația punctată așa cum se arată în exemplul de mai sus, status.success și status.greeting.

închideri

Închidere în Swift sunt aceleași cu blocurile din Obiectiv-C. Acestea pot fi definite în linie, trecute ca parametru sau returnate prin funcții. Le folosim exact așa cum am folosi blocurile în Obiectiv-C.

De asemenea, definirea închiderilor este ușoară. De fapt, o funcție este un caz special de închidere. Deci, nu e de mirare că definirea unei închideri arată foarte mult ca definirea unei funcții.

Închideri sunt un tip de primă clasă, ceea ce înseamnă că pot fi transferate și returnate prin funcții la fel ca orice alt tip, cum ar fi IntŞirbool, închiderile sunt în esență blocuri de cod care pot fi numite mai târziu și au acces la domeniul în care au fost definite.

Crearea unei închideri fără nume este la fel de simplă ca înfășurarea unui bloc de cod în bretele curbate. Parametrii și tipul de întoarcere a închiderii sunt separați de corpul închizătorului cu în cuvinte cheie.

Să presupunem că vrem să definim o închidere care revine Adevărat dacă un număr este egal, atunci închiderea ar putea părea ceva asemănător:

este isEven = (number: Int) -> Bool înăuntă mod = numărul% 2 return (mod == 0)

este chiar închiderea durează un an Int ca parametru unic și returnează a bool. Tipul acestei închideri este (numărul: Int) -> Bool, sau (Int -> Bool) pe scurt. Putem suna este chiar oriunde în codul nostru, așa cum am invoca un bloc de cod în Obiectiv-C.

Pentru a trece o închidere de acest tip ca parametru al unei funcții, folosim tipul de închidere în definiția funcției:

este isEven = (number: Int) -> Bool in let mod = numărul% 2; retur (mod == 0);  func verificIfEven (număr: Int, verificator: (Int-> Bool)) -> Bool return verificator (număr);  verifyIfEven (12, esteEven); // returnează adevărat checkIfEven (19, isEven); // returnează false

În exemplul de mai sus, verificator parametru al verifyIfEven funcția este o închidere pe care o trecem la funcție.

3. Clase și structuri

Clase

Este timpul să vorbim despre piatra de temelie a programelor orientate pe obiecte, clase. Clasele, așa cum au fost menționate mai sus, sunt definite într - un singur fișier de implementare cu un .rapid extensie. Declarațiile și metodele de proprietate sunt definite în fișierul respectiv.

Creați o clasă cu clasă cuvânt cheie urmat de numele clasei. Implementarea clasei este înfășurată într-o pereche de bretele curbate. Ca și în Obiectiv-C, convenția de numire pentru clase este de a folosi cazul cămășii superioare pentru numele de clasă.

hotel clasa // proprietăți // funcții

Pentru a crea o instanță a Hotel clasa scrie:

h h = Hotel ()

În Swift, nu este nevoie să sunați init pe obiecte ca init se numește automat pentru noi.

Clasa de moștenire urmează același model ca și în Obiectiv-C, un colon separă numele clasei și cel al superclasei. În exemplul următor, Hotel moșteneste de la BigHotel clasă.

clasa BigHotel: Hotel 

Ca și în Obiectiv-C, folosim notația punctului pentru a accesa proprietățile obiectului. Cu toate acestea, Swift utilizează notația punctului pentru a invoca metodele de clasă și instanță, după cum puteți vedea mai jos.

// Obiectiv-C UIView * vizualizare = [[UIView alloc] init]; [self.view addSubview: vizualizare]; / / Swift permite vizualizarea = UIView () self.view.addSubview (vizualizare)

Proprietăți

O altă diferență cu obiectivul C este că Swift nu distinge între variabilele de instanță (ivars) și proprietățile. O variabilă de instanță este o proprietate.

Declararea unei proprietăți este la fel ca definirea unei variabile sau a unei constante, folosind var și lăsa Cuvinte cheie. Singura diferență este contextul în care acestea sunt definite, adică contextul unei clase.

class Hotel săli camere = 10 var fullRooms = 0

În exemplul de mai sus, camere este o valoare imuabilă, o constantă 10 și fullRooms este o variabilă cu o valoare inițială de 0, pe care le putem schimba mai târziu. Regula este că proprietățile trebuie inițializate atunci când sunt declarate. Singura excepție de la această regulă sunt opțiunile, pe care le vom discuta într-un moment.

Proprietăți calculate

Limbajul Swift definește și proprietățile calculate. Proprietățile computerizate nu sunt nimic mai mult decât getters și setteri fantezie care nu stochează o valoare. După cum indică și numele, acestea sunt calculate sau evaluate în zbor.

Mai jos este un exemplu de proprietate calculată. Am schimbat-o camere proprietate la var pentru restul acestor exemple. Veți afla de ce mai târziu.

hotel clasa var camere = 10 var fullRooms = 0 var descriere: String get return Dimensiune hotel: \ (camere) capacitati camere: \ (fullRooms) / \ (camere) "

Deoarece Descriere proprietatea este doar pentru citire și are doar întoarcere declarație, putem omite obține cuvânt cheie și acolade bretele, și să păstreze numai întoarcere afirmație. Aceasta este o stenogramă și asta voi folosi în restul acestui tutorial.

hotel clasa var camere = 10 var fullRooms = 0 var descriere: String return "Dimensiune hotel: \ (camere) capacitati camere: \ (fullRooms) / \ (camere)"

De asemenea, putem defini proprietăți computerizate citire-scriere. In clasa noastra Hotel, vrem un an emptyRooms proprietate care primește numărul de camere goale în hotel, dar dorim și să ne actualizăm fullRooms când am setat emptyRooms proprietate calculată. Putem face acest lucru folosind a stabilit după cum se arată mai jos.

hotel clasa var camere = 10 var fullRooms = 0 var descriere: String return "Dimensiune hotel: \ (camere) capacitati camere: \ (fullRooms) / \ (rooms)" var emptyRooms: fullRooms set // constantă newValue este disponibilă aici // care conține valoarea transmisă dacă (newValue < rooms)  fullRooms = rooms - newValue  else  fullRooms = rooms     let h = Hotel() h.emptyRooms = 3 h.description // Size of Hotel: 10 rooms capacity:7/10

În emptyRooms setter, newValue constanta ne este predata si reprezinta valoarea transferata catre setter. De asemenea, este important să rețineți că proprietățile calculate sunt întotdeauna declarate ca variabile, folosind var cuvânt cheie, deoarece valoarea lor calculată se poate schimba.

metode

Am acoperit deja funcțiile din acest articol. Metodele nu sunt altceva decât funcții care sunt legate de un tip, cum ar fi o clasă. În următorul exemplu vom implementa o metodă instanță, bookNumberOfRooms, în Hotel pe care am creat-o mai devreme.

hotel clasa var camere = 10 var fullRooms = 0 var descriere: String return "Dimensiune hotel: \ (camere) capacitati camere: \ (fullRooms) / \ (rooms)" var emptyRooms: fullRooms set // constantă newValue este disponibilă aici // care conține valoarea transmisă dacă (newValue < rooms)  fullRooms = rooms - newValue  else  fullRooms = rooms    func bookNumberOfRooms(room:Int = 1) -> Bool if (self.emptyRooms> room) auto.fullRooms ++; return true else return false h = hotel () h.emptyRooms = 7 h.description // Dimensiune hotel: 10 camere capacitate: 3/10 h.bookNumberOfRooms (cameră: 2) .description // Dimensiune hotel: 10 camere capacitate: 5/10 h.bookNumberOfRoom () // returneaza descrierea adevarata // Dimensiune hotel: 10 camere capacitate: 6/10 

initializatori

Initializatorul implicit pentru clase este init. În init , setăm valorile inițiale ale instanței create.

De exemplu, dacă am avea nevoie de a Hotel subclasa cu 100 de camere, atunci am avea nevoie de un inițializator pentru a seta camere proprietate la 100. Amintiți-vă că am schimbat anterior camere de la a fi o constantă a fi o variabilă în Hotel clasă. Motivul este că nu putem schimba constantele moștenite într-o subclasă, doar variabilele moștenite pot fi schimbate.

clasa BigHotel: Hotel init () super.init () camere = 100 bh = BigHotel () println (bh.description); // Dimensiune hotel: 100 camere capacitate: 0/100

Inițializatorii pot lua, de asemenea, parametri. Următorul exemplu vă arată cum funcționează acest lucru.

clasa CustomHotel: Hotel init (dimensiune: Int) super.init () camere = dimensiune c = CustomHotel (dimensiune: 20) c.description // Dimensiune hotel: 20 camere capacitate: 0/20

Metode de overriding și proprietăți computerizate

Acesta este unul dintre cele mai tari lucruri din Swift. În Swift, o subclasă poate suprascrie atât metodele, cât și proprietățile calculate. Pentru a face acest lucru, folosim trece peste cuvinte cheie. Hai să ignorăm Descriere proprietate calculată în CustomHotel clasă:

clasa CustomHotel: Hotel init (dimensiune: int) super.init () camere = dimensiune suprascriere var descriere: String return super.description + "Howdy!"  c = CustomHotel (dimensiune: 20) c.description // Dimensiune hotel: 20 camere capacitate: 0/20! 

Rezultatul este că Descriere returnează rezultatul superclass Descriere cu șirul "Salutare!" anexat la acesta.

Ce este cool despre metodele imperative și proprietățile calculate este trece peste cuvinte cheie. Când compilatorul vede trece peste cuvântul cheie, verifică dacă superclama clasei implementează metoda care este suprascrisă. Compilatorul verifică, de asemenea, dacă proprietățile și metodele unei clase sunt în conflict cu proprietățile sau metodele de mai sus în arborele de mostenire.

Nu știu de câte ori o tipografie într-o metodă suprascrisă în Obiectiv-C ma făcut să blestem, pentru că codul nu funcționa. În Swift, compilatorul vă va spune exact ce este în neregulă în aceste situații.

structuri

Structuri, definite cu struct , sunt mai puternice în Swift decât în ​​C și Obiectiv-C. În C, structura definește numai valori și indicatori. Structurile Swift sunt la fel ca structurile C, dar ele sprijină, de asemenea, proprietățile și metodele computerizate.

Orice puteți face cu o clasă, puteți face o structură, cu două diferențe importante:

  • structurile nu susțin moștenirea ca și clasele
  • structurile sunt transmise prin valoare, în timp ce clasele sunt transmise prin referință

Iată câteva exemple de structuri în Swift:

struct Rect var orig: Punct var dimensiune: Dimensiune var area: Dublu return size.width * size.height func esteBiggerThanRect (r: Rect) -> Bool return (self.area> r.area) var x = 0 var y = 0 struct Dimensiunea var width = 0 var height = 0

4. Opțiuni

Soluție la o problemă

Opțiunile sunt un concept nou dacă vin din Obiectiv-C. Ei rezolvă o problemă cu care ne confruntăm toți ca programatori. Când accesăm o variabilă a cărei valoare nu suntem siguri, returnăm de obicei un indicator, cunoscut ca un santinel, pentru a indica faptul că valoarea returnată este o valoare fără valoare. Permiteți-mi să ilustrez acest lucru cu un exemplu din obiectivul C:

NSString * someString = @ "ABCDEF"; NSInteger pos = [someString rangeOfString: @ "B"] locație; // pos = 1

În exemplul de mai sus, încercăm să găsim poziția @ "B" în someString. Dacă @ "B" se găsește, locația sau poziția sa este stocată în pos. Dar ce se întâmplă dacă @ "B" nu este găsit în someString?

Documentația menționează că rangeOfString: returnează un NSRange cu Locație setați la NSNotFound constant. În cazul în care rangeOfString:, Sentinelul este NSNotFound. Sentinele sunt folosite pentru a indica faptul că valoarea returnată nu este validă.

În Cocoa, există multe utilizări ale acestui concept, dar valoarea santinelă diferă de context în context, 0, -1, NUL, NSIntegerMax, INT_MAX, Zero, etc. Problema pentru programator este că ea trebuie să-și amintească ce sentinel este folosit în ce context. Dacă programatorul nu este atent, poate gresi o valoare validă pentru un santinel și invers. Swift rezolvă această problemă cu opțiunile opționale. Pentru a cita Brian Lanier "Opționalii sunt santinelul pentru a le conduce pe toți".

Opțiunile au două stări, a zero stat, ceea ce înseamnă că opțiunea nu conține nici o valoare, și o a doua stare, ceea ce înseamnă că ea deține o valoare validă. Gândiți-vă la opțiuni ca un pachet cu un indicator care să vă spună dacă conținutul pachetului este valabil sau nu.

folosire

Toate tipurile din Swift pot deveni opționale. Definim un opțional prin adăugarea unui a ? dupa declaratia de tip asa:

hai să intrăm: Int? // someInt == zero

Atribuiți o valoare pachetului opțional la fel cum facem cu constantele și variabilele.

someInt = 10 // someInt! == 10

Amintiți-vă că opțiunile sunt asemănătoare pachetelor. Când am declarat lăsați-l pe el: Int?, am definit o cutie goală cu o valoare de zero. Prin atribuirea valorii 10 la caseta opțională, caseta conține un număr întreg egal cu 10 și indicatorul său sau starea devine nu zero.

Pentru a ajunge la conținutul unui opțional folosim ! operator. Trebuie să fim siguri că opțiunea are o valoare validă înainte de ao despacheta. Dacă nu faceți acest lucru, va apărea o eroare de execuție. Acesta este modul în care accesăm valoarea stocată într-o opțiune:

dacă someInt! = nil) println ("someInt: \ (someInt!)") altfel println ("someInt nu are valoare") // someInt:

Modelul de mai sus este atât de comun în Swift, încât putem simplifica blocul de cod de mai sus folosind legare opțională cu dacă lăsați Cuvinte cheie. Uitați-vă la blocul de cod actualizat de mai jos.

dacă valoarea = someInt println ("someInt: \ (value)") altceva println ("someInt nu are valoare")

Opțiunile sunt singurele tipuri care pot dura zero valoare. Constantele și variabilele nu pot fi inițializate sau setate la zero. Aceasta face parte din politica de siguranță a Swift, toate variabilele și constantele non-opționale trebuie sa au o valoare.

5. Managementul memoriei

Dacă vă aduceți aminte, înapoi când ARC a fost introdus, noi folosim puternic și slab cuvinte cheie pentru a defini proprietatea obiectului. Swift are, de asemenea, a puternic și slab modelul de proprietate, dar introduce și unul nou, unowned. Să aruncăm o privire asupra fiecărui model de proprietate asupra obiectelor din Swift.

puternic

Referințele puternice sunt implicit în Swift. De cele mai multe ori, deținem obiectul pe care îl referăm și suntem cei responsabili pentru menținerea obiectului referit în viață.

Deoarece referințele puternice sunt implicite, nu este necesar să păstrați în mod explicit o referință puternică la un obiect, orice referință este o referință puternică.

slab

O referință slabă în Swift indică faptul că referința indică un obiect că nu suntem responsabili pentru păstrarea vieții. Este folosit în principal între două obiecte care nu au nevoie de cealaltă pentru a fi în jur pentru ca obiectul să-și continue ciclul de viață.

Există unul dar, in orice caz. În Swift, referințele slabe trebuie să fie întotdeauna variabile cu un tip opțional, deoarece acestea sunt setate la zero când obiectul referit este dealocat. slab cuvântul cheie este folosit pentru a declara o variabilă ca slabă:

slab var vizual: UIView?

unowned

Referințele neavizate sunt noi pentru programatorii Obiectiv-C. O referință necunoscută înseamnă că nu suntem responsabili pentru păstrarea în viață a obiectului referit, la fel ca trimiterile slabe.

Diferența cu o referință slabă este că o referință necotate nu este setată la zero când obiectul pe care îl trimite este dealocat. O altă diferență importantă, cu referințe slabe, este că referințele necotate sunt definite ca un tip non-opțional.

Referințele neavizate pot fi constante. Un obiect neînvecinat nu există fără proprietarul său și, prin urmare, referința neamenajată nu este niciodată zero. Referințele nefiind necesare unowned înainte de definirea variabilei sau a constantei.

unowned var vedere: UIView

Concluzie

Swift este un limbaj uimitor care are o mulțime de profunzime și potențial. Este distractiv să scriem programe și elimină o mulțime de codul de boilerplate pe care îl scriem în Obiectiv-C pentru a ne asigura că codul nostru este sigur.

Vă recomandăm limba limbajului de programare Swift, care este disponibil gratuit în magazinul iBooks al Apple.

Cod