TEAM LICENSES: Save money and learn new skills through a Hacking with Swift+ team license >>

Clase

În Swift mai este un mod de a crea tipuri de date complexe numite clase. Ele arată similar cu structurile, dar au un număr de diferențe importante, printre care:

  • Nu ai inițializator per membru automat pentru clasele tale; trebuie să le scrii tu însuți.
  • Poți să definești o clasă ca fiind bazată pe o altă clasă, adăugând orice lucruri noi dorești.
  • Când creezi o instanță a unei clase, aceasta se numește un obiect. Dacă copiezi un obiect, ambele copii au referință aceleași date în mod implicit – dacă modifici una, copia se va modifica și ea.

Toate cele trei sunt diferențe mari, deci o sa le acopăr în amănunt înainte să continuăm.

Inițializarea unui obiect

Dacă am converti structura Person într-o clasă Person, Swift nu ne va lăsa să facem asta:

class Person {
    var clothes: String
    var shoes: String
}

Asta din cauză că declarăm cele două proprietăți să fie de tipul String, ceea ce dacă îți amintești trebuie neapărat să aibă o valoare. Asta era în regulă într-o structură deoarece Swift produce un inițializator per membru în mod automat, forțându-ne să furnizăm valori pentru cele două proprietăți, dar acest lucru nu se întâmplă în mod automat cu clasele în, deci Swift nu poate fi sigur că ele vor avea valori.

Există trei soluții: fă cele două valori stringuri opționale, dă-le valori implicite, sau să scriem propriul nostru inițializator. Prima metodă este stângace deoarece introduce opționale peste tot în codul nostru unde nu trebuie să existe. A doua opțiune funcționează, dar estre puțin inutilă dacă acele valori implicite nu vor fi folosite. Ne mai rămâne ultima opțiune, și aceasta este cea corectă: să scriem propriile inițializatoare.

Pentru a face asta, crează o metodă înăuntrul clasei numită init() care primește cei doi parametrii care ne interesează:

class Person {
    var clothes: String
    var shoes: String

    init(clothes: String, shoes: String) {
        self.clothes = clothes
        self.shoes = shoes
    }
}

Sunt două lucruri care se poate să-ți sară în vedere.

Primul, nu scrii cuvântul func înaintea metodei init() , deoarece ea este specială. Al doilea, deoarece numele parametrilor sunt aceleași cu numele proprietăților cărora vrem să le atribuim, folosești self. pentru a te face în'eles – "proprietatea clothes a obiectului ar trebui să fie cea a parametrului de intrare clothes ." Dacă dorești poți să le dai nume unice – depinde doar de tine.

Important: Swift arecerința ca toate valoriile non-opționale să aibă o valoare până la sfârșitul inițializatorului, sau când inițializatorul apelează oricare altă metodă – oricare va fi prima.

Moștenirea

A doua diferență dintre clase și structuri este aceea că clasele se pot construi una pe cealaltă pentru a creea lucruri mai mare, numită și moștenire. Aceasta este o tehnică folosită intens în Cocoa Touch, chiar și în cele mai fundamentale programe, deci este ceva ce ar trebui să exersezi.

Să începem cu ceva simplu: o clasă Singer care are ca proprietăți numele și vârsta . Cât despre metode, va fi un simplu inițializator care va seta proprietățile, plus o metodă sing() care va produce niște cuvinte:

class Singer {
    var name: String
    var age: Int

    init(name: String, age: Int) {
        self.name = name
        self.age = age
    }

    func sing() {
        print("La la la la")
    }
}

Acum putem crea o instanță a acelui obiect apelând inițializatorul, apoi îi putem citi proprietățile și apela metodele:

var taylor = Singer(name: "Taylor", age: 25)
taylor.name
taylor.age
taylor.sing()

Asta este clasa noastră de bază, dar vom construi pe ea: Vreau să definesc o clasă CountrySinger care are tot ceea ce clasa Singer are, dar când apelez sing() vreau să afișeze "Trucks, guitars, and liquor" în schimb.

Ai putea, bine înteles să copiezi clasa originală Singer și să o aplici unei noi clase CountrySinger dar acesta este un mod leneș de a programa și se va întoarce împotriva ta când vei face modificări ulterioare clasei Singer și vei uita să le copiezi. În loc, Swift are o soluție mai inteligentă: putem defini clasa CountrySinger ca fiind bazată pe Singer și va primi toate proprietățile și metodele pentru a putea construi pe ele:

class CountrySinger: Singer {

}

Cele doup punte produc magia: Înseamnă că "CountrySinger extinde Singer." Acum, această nouă clasă CountrySinger (numită subclasă) nu adaugă nimic clasei Singer (numită clasă părinte, sau superclasă) încă. Vrem ca ea să aibă propria metodă sing(), dar în Swift trebuie să înveți un cuvânt nou: override. Asta înseamnă "Știu că această metodă a fost implementată în clasa părinte, dar vreau să o modific pentru această subclasă."

Având cuvântul cheie override este de ajutor, deoarece îți face intenția clară. Îi permite lui Swift să îți verifice codul: dacă nu folosești override Swift nu te va lăsa să modifici metoda pe care ai obținut-o de la superclasă, sau dacă folosești override și nu este nimic de suprascris, Swift îți va arăta eroarea.

Deci, trebuie să folosim override func, așa:

class CountrySinger: Singer {
    override func sing() {
        print("Trucks, guitars, and liquor")
    }
}

Acum modifică modul în care obiectul taylor este creat:

var taylor = CountrySinger(name: "Taylor", age: 25)
taylor.sing()

Dacă modifici CountrySinger în Singer ar trebui să vezi mesajele diferite apărând în panoul cu rezultate.

Acum, pentru a complica lucrurile, vom defini o nouă clasă numită HeavyMetalSinger. Dar de data aceasta vom stoca o nouă proprietate numită noiseLevel definind cât de tare acest cântăreț de heavy metal țipă în microfon.

Aici apare o problemă, și este una care trebuie rezolvată într-un anumit mod:

  • Swift vrea ca toate proprietățile non-opționale să aibă o valoare.
  • Clasa noastră Singer nu are o proprietate noiseLevel.
  • Deci, trebuie să creăm un inițializator personalizat pentru HeavyMetalSinger care acceptă un nivel de zgomot.
  • Acel nou inițializator trebuie de asemenea să știe proprietățile name și age ale cântărețului de heavy metal, pentru a le putea trimite superclasei Singer.
  • Trimiterea datelor superclasei se face prin apelarea unei metode, și nu poți apela metode în inițializatori până când nu ai atribuit valori tuturor proprietăților.
  • Deci, trebuie să setăm propriile proprietăți mai întâi (noiseLevel) apoi să transmitem ceilalți parametrii superclasei.

Asta poate suna groaznic de complicat, însă codul este simplu. Uite clasa HeavyMetalSinger, completă cu propria metodă sing():

class HeavyMetalSinger: Singer {
    var noiseLevel: Int

    init(name: String, age: Int, noiseLevel: Int) {
        self.noiseLevel = noiseLevel
        super.init(name: name, age: age)
    }

    override func sing() {
        print("Grrrrr rargh rargh rarrrrgh!")
    }
}

Observă că inițializatorul acceptă trei parametrii, apoi apelează super.init() pentru a transmite name și age la superclasa Singer - dar numai după ce propria proprietate a fost setată. Vei vedea super folosit de multe ori în obiecte, și asta înseamnă doar "apelează o metodă din clasa moștenită.” De obicei este folosit pentru a spune "lasă clasa părinte să facă orice are nevoie să facă mai înâi, apoi voi face ce vreau eu extra."

Moștenirea este un subiect mare deci nu te necăji dacă nu este totul clas încă. Oricum, mai este un lucru pe care trebuie să-l știi: moștenirea de multe ori se petrece pe multe nivele. De exemplu, A poate moșteni pe B, și B poate moșteni pe C, și C poate moșteni pe D, și așa mai departe. Asta îți permite să construiești funcționalitate și să refolosești un număr de clase, ajutânddu-te să menții un cod modular și ușor de înțeles.

Lucrând cu cod Objective-C

Dacă vrei ca o parte din sistemul de operare Apple să îți apeleze o metodă a unei clase Swift, trebuie să o marchezi cu un atribut special: @objc. Înseamnă pe scurt “Objective-C”, și atributul marchează această metodă ca fiind disponibilă pentru codul Objective-C mai vechi – adică aproape tot codul iOS, macOS, watchOS, and tvOS. De exemplu, dacă îi ceri sistemului să îți apeleze metoda după o secundă, va trebui să o marchezi cu @objc.

Nu-ți fă prea multe probleme în legătură cu @objc pentru moment – nu doar că îți voi explica mai târziu, dar Xcode îți va spune întotdeauna când este necesar. În mod contrar, dacă nu vrei să folosești @objc pentru anumite metode poți să pui @objcMembers înaintea clasei pentru a face toate metodele disponibile pentru Objective-C.

Valori vs Referințe

Când copiezi o structură, ea este duplicată împreună cu toate valorile ei. Ceea ce înseamnă că dacă modifici o copie a unei structuri, celelalte copii vor rămâne neschimbate – toate sunt individuale. În cazul claselor, fiecare copie a unui obiect are referință la obiectul original, deci dacă vei modifica una, toate se vor modifica. Swift numește structuriile "tipuri valoare" deoarece ele referențiază doar o valoare, și clasele "tipuri referință" deoarece obiectele sunt doar referințe ale valorii reale.

Aceasta este o diferență importantă, ceea ce înseamnă că alegerea dintre structuri și clase este una importantă:

  • Dacă vrei o stare comună pe care o poți transmite și modifica, ai nevoie de o clasă. Poți să le trimiți în funcții sau să le stochezi în array-uri, să le modifici acolo și acele modificări să se reflecte în tot restul programului tău.
  • Dacă vrei să eviți starea comună și o copie să nu le poată afecta pe toate celelalte, ai nevoie de structuri. Poți să le transmiți în funcții sau le poți stoca în array-uri, modifica acolo și ele nu se vor modifica oriunde altundeva sunt folosite.

Dacă ar fi să fac un rezumat al diferenței dintre structuri și clase, aș spune asta: clasele oferă mai multă flexibilitate, iar structurile oferă mai multă siguranță. Ca și regulă de bază, ar trebui să folosești întotdeauna structuri dacă nu ai un motiv specific să folosești clase.

Hacking with Swift is sponsored by Blaze.

SPONSORED Still waiting on your CI build? Speed it up ~3x with Blaze - change one line, pay less, keep your existing GitHub workflows. First 25 HWS readers to use code HACKING at checkout get 50% off the first year. Try it now for free!

Reserve your spot now

Sponsor Hacking with Swift and reach the world's largest Swift community!

BUY OUR BOOKS
Buy Pro Swift Buy Pro SwiftUI Buy Swift Design Patterns Buy Testing Swift Buy Hacking with iOS Buy Swift Coding Challenges Buy Swift on Sundays Volume One Buy Server-Side Swift Buy Advanced iOS Volume One Buy Advanced iOS Volume Two Buy Advanced iOS Volume Three Buy Hacking with watchOS Buy Hacking with tvOS Buy Hacking with macOS Buy Dive Into SpriteKit Buy Swift in Sixty Seconds Buy Objective-C for Swift Developers Buy Beyond Code

Was this page useful? Let us know!

 
Unknown user

You are not logged in

Log in or create account
 

Link copied to your pasteboard.