Polish (Polski) translation by Agnieszka Górczyńska (you can also view the original English article)
W poprzednim artykule poświęconym wprowadzeniu do języka programowania Swift skoncentrowałem się głównie na założeniach języka, przedstawiłem pokrótce jego składnię oraz wyszczególniłem kilka podstawowych różnic w stosunku do Objective-C. W tym artykule będę kontynuował omawianie składni języka Swift. Ponadto poruszę temat optionals oraz zarządzania pamięcią.
1. Konstrukcje warunkowe i pętle
Polecenie if
Polecenie if w języku Swift jest niemal identyczne z istniejącym w Objective-C. W stosunku do Objective-C występują tylko dwie subtelne różnice:
- nawiasy okrągłe wokół zmiennej warunku są opcjonalne;
- nawiasy klamrowe są wymagane.
Powyższe punkty to jedyne różnice między poleceniami if w obu wymienionych językach.
Zakresy
Jak zobaczyłeś w poprzednim artykule, Swift oferuje dwa rodzaje operatorów (..<
i ...
) przeznaczonych do definiowania zakresu wartości. Pierwszy z nich nosi nazwę pół-zamkniętego operatora zakresu, natomiast drugi zamkniętego operatora zakresu.
Pół-zamknięty operator zakresu, na przykład 1..<5
przedstawia wartości 1, 2, 3, i 4 (nie obejmuje wartości 5). Z kolei zamknięty operator zakresu, taki jak 1...5
przedstawia wartości 1, 2, 3, 4 i tutaj obejmuje 5.
Zakresy mogą zostać użyte w pętlach for
, tablicach (array
), a nawet w konstrukcjach switch
. Spójrz na przedstawione poniżej przykłady.
// for loop example for i in 1..<10 { } // iterates from 1 to 9
// array subscript example let someArray = ["apple","pair","peach","watermelon","strawberry"] for fruit in someArray[2..<4] { println(fruit) } // outputs: peach and watermelon
// switch example switch someInt { case 0: // do something with 0 case 1..<5: // do something with 1,2,3,4 case 5...10: // do something with 5,6,7,8,9,10 default: // everything else }
Konstrukcja switch
Konstrukcja switch ma znacznie większe możliwości w języku Swift niż w Objective-C. W Objective-C wynikiem wyrażenia polecenia switch musi być liczba całkowita, a wartości poszczególnych poleceń typu case powinny być stałą. Natomiast w przypadku języka Swift sprawa przedstawia się inaczej. Polecenia case mogą być dowolnego typu włączając w to zakresy.
W języku Swift konstrukcja switch nie posiada poleceń break
i nie następuje automatyczne przejście od jednego bloku case do następnego. Tworząc polecenie switch należy szczególną uwagę zwrócić na to, aby wszystkie możliwe warunki
zostały spełnione przez bloki case. Zaniedbanie tego wymogu
skutkuje powstaniem błędu w trakcie kompilacji. Najpewniejszym sposobem uwzględnienia wszystkich warunków jest dołączenie bloku default
.
Poniżej przedstawiono przykład konstrukcji switch
zawierającej bloki case typu String
:
let vegetable = "red pepper" switch vegetable { case "celery": let vegetableComment = "Add some raisins and make ants on a log." case "cucumber", "watercress": let vegetableComment = "That would make a good tea sandwich." default: let vegetableComment = "Everything tastes good in soup." }
W języku Swift nie następuje domyślne przejście do następnego polecenia case po wykonaniu poprzedniego. To jest celowa decyzja projektowa, która ma pomóc
programistom w unikaniu często popełnianych błędów. Jeśli dane polecenie case ma być wykonane, wówczas można użyć słowa kluczowego fallthrough
w celu wskazania tego faktu kompilatorowi.
switch someInt { case 0: // do something with 0 case 1: // do something with 1 case 2: // do something with 2 fallthrough default: //do something for everything else } // case 2 will fall through to default case
Na tym jednak nie poprzestajemy. W języku Swift pojawiły się jeszcze dwie inne funkcje konstrukcji switch: dołączanie wartości i klauzula where. Wartości są dołączane za pomocą słów kluczowych caselet
umożliwiających przyporządkowanie stałej do dopasowanego polecenia case. Z kolei klauzula where umożliwia dodawanie za pomocą słowa kluczowego where
specjalnego warunku do polecenia case.
Te dwie koncepcje najlepiej można wyjaśnić za pomocą przykładów. Poniższy blok kodu pokazuje, w jaki sposób działa dołączanie wartości.
let somePoint = (xaxis:2, yaxis:0) switch somePoint { case (let x, 0): println("on the x-axis with an x value of \(x)") case (0, let y): println("on the y-axis with a y value of \(y)") case let (x, y): println("somewhere else at (\(x), \(y))") }
W powyższym fragmencie kodu pierwsze polecenie case - case (let x, 0)
- dopasowuje wartości, gdzie xaxis
ma dowolną wartość, natomiast yaxis
ma wartość 0
. Ponadto dołączamy xaxis
do stałej x
w poleceniu case.
Poniżej został zaprezentowany przykład praktycznego użycia klauzuli where.
let vegetable = "red pepper" switch vegetable { case "celery": println("Add some raisins and make ants on a log.") case let x where x.hasSuffix("pepper"): println("I'm allergic to \(x)") default: println( "Everything tastes good in soup.") } // outputs: I'm allergic to red pepper
2. Funkcje i domknięcia
Funkcje
W języku Swift definiowanie funkcji i domknięć jest bardzo proste. Programiści, którzy mają doświadczenie w tworzeniu kodu w Objective-C znają te pojęcia jako funkcje i bloki.
W przypadku Swifta parametry funkcji mogą posiadać wartości domyślne, co przypomina nieco języki skryptowe, takie jak PHP i Ruby.
Składnia funkcji przedstawia się następująco:
func functionName(parameterName: Type = DefaultValue) -> returnType { [...] return returnType; }
Poniżej przedstawiono kod funkcji sayHello()
przyjmującej parametr name
typu String
. Wartością zwrotną funkcji jest wartość boolowska (Bool
).
func sayHello(name: String) -> Bool { println("hello \(name)"); return true; } sayHello("World") // output // hello World
W celu przekazania wartości domyślnej dla parametru name
implementacja funkcji może przedstawiać się następująco:
func sayHello(name: String = "World") -> Bool { println("hello \(name)"); return true; } sayHello() // output // hello World sayHello("mike") // output // hello mike
Elementem, który nie występuje w Objective-C jest krotka. W języku Swift funkcje mogą zwracać wiele wartości w formie krotki. Wspomniana krotka jest traktowana jako pojedyncza zmienna, co oznacza możliwość przekazania jej w dokładnie taki sam sposób, jak pojedynczej zmiennej.
Krotka jest bardzo prosta w użyciu. Właściwie omówiłem już jej działanie w poprzednim artykule przy okazji omawiania słowników. W poniższym fragmencie kodu, para klucz-wartość jest krotką.
for (key,value) in someDictionary { println("Key \(key) has value \(value)" }
Być może zastanawiasz się, jak używać krotki i
jakie korzyści można osiągnąć z jej stosowania? Spójrzmy na inny przykład. Zmodyfikujemy powyższą funkcję sayHello()
w taki sposób, że gdy wartością zwrotną jest true, wtedy zostanie do niej dołączony komunikat. W tym celu wykorzystamy krotkę (Bool, String)
. Zmodyfikowana funkcja sayHello()
przedstawia się następująco:
func sayHello(name: String = "World") -> (Bool, String) { let greeting = "hello \(name)" return (true, greeting); } let (success, greeting) = sayHello() println("sayHello returned success:\(success) with greeting: \(greeting)"); // output // sayHello returned success:1 with greeting: hello World
Już od dłuższego czasu krotka znajdowała się na liście życzeń wielu programistów tworzących kod w Objective-C.
Kolejną bardzo istotną korzyścią wynikającą z użycia krotki jest możliwość nazwania zwracanych zmiennych. Jeśli w poprzednim przykładzie nadamy nazwy zmiennym w krotce, wówczas kod funkcji będzie miał następującą postać:
func sayHello(name: String = "World") -> (success: Bool, greeting: String) { let greeting = "hello \(name)" return (true, greeting); } let status = sayHello() println("sayHello returned success:\(status.success) with greeting: \(status.greeting)"); // output // sayHello returned success:1 with greeting: hello World
Oznacza to, że zamiast definiowania oddzielnych stałych dla poszczególnych elementów krotki, dostęp do nich możemy uzyskać za pomocą notacji z kropką, na przykład status.success
i status.greeting
.
Domknięcia
Domknięcia w języku Swift są tym samym, czym bloki w Objective-C. Mogą być definiowane w miejscu ich wykonania, przekazane jako parametr lub zwrócone przez funkcje. Używamy ich dokładnie w taki sam sposób, jak bloki w Objective-C.
Również definiowanie domknięć jest proste. Tak naprawdę funkcja jest szczególnym przypadkiem domknięcia. Nie dziwi więc fakt, że definiowanie domknięcia przypomina definiowanie funkcji.
Domknięcia są typem pierwszej klasy, co oznacza, że mogą być przekazywane i zwracane przez funkcje dokładnie tak samo, jak każdy inny typ, na przykład: Int
, String
, Bool
itd. Domknięcia są w gruncie rzeczy blokami kodu, można je późniejwywoływać i mieć dostęp do zakresu, w którym zostały zdefiniowane.
Utworzenie nienazwanego domknięcia jest proste, i
sprowadza się do ujęcia bloku kodu w nawias klamrowy. Parametry i typ wartości zwrotnej domknięcia są oddzielone słowem kluczowym in
od części głównej domknięcia.
Przyjmujemy założenie, że chcemy zdefiniować domknięcie o wartości zwrotnej true
, jeżeli liczba będzie parzysta. Tego rodzaju domknięcie mogłoby przedstawiać się następująco:
let isEven = { (number: Int) -> Bool in let mod = number % 2 return (mod==0) }
Domknięcie isEven
przyjmuje pojedynczy parametr w postaci wartości typu Int
. Natomiast wartością zwrotną domknięcia jest wartość boolowska (Bool
). Typ omawianego domknięcia to (number: Int) - > Bool
, lub w skrócie (Int - > Bool)
. Domknięcie isEven
możemy wywołać w dowolnym miejscu kodu, podobnie jak blok kodu w Objective-C.
Aby tego rodzaju domknięcie mogło być przekazane jako parametr funkcji, jego typ trzeba podać w definicji funkcji:
let isEven = { (number: Int) -> Bool in let mod = number % 2; return (mod==0); } func verifyIfEven(number: Int, verifier:(Int->Bool)) ->Bool { return verifier(number); } verifyIfEven(12, isEven); // returns true verifyIfEven(19, isEven); // returns false
W powyższym przykładzie parametr verifier
funkcji verifyIfEven()
jest domknięciem przekazanym funkcji.
3. Klasy i struktury
Klasy
Przechodzimy teraz do kamienia węgielnego programowania zorientowanego obiektowo, czyli do klas. Jak wcześniej wspomniano, klasy są definiowane w pojedynczym pliku implementacji o rozszerzeniu .swift. Ten plik zawiera także deklaracje i metody.
Utworzenie klasy następuje za pomocą słowa kluczowego class
, po którym należy podać nazwę klasy. Implementacja klasa znajduje się wewnątrz nawiasu
klamrowego. Podobnie jak w Objective-C, także w języku Swift
nazwy klasy stosują styl CamelCase i rozpoczynają się dużą literą.
class Hotel { //properties //functions }
W celu utworzenia egzemplarza klasy Hotel
należy wydać poniższe polecenie:
let h = Hotel()
W języku Swift nie ma konieczności wywoływania metody init()
w obiektach, ponieważ wymieniona metoda jest wywoływana automatycznie.
Dziedziczenie klas podlega takiemu samemu wzorcowi,
jak zastosowany w Objective-C. Do rozdzielenia nazw klas definiowanej i nadrzędnej używany jest dwukropek. W poniższym przykładzie klasa Hotel
dziedziczy po klasie BigHotel
.
class BigHotel: Hotel { }
Podobnie jak w Objective-C, w celu uzyskania dostępu do właściwości obiektu wykorzystywana jest składnia z kropką. Jednak Swift używa składni kropki także do wywoływania klas i metod egzemplarza, o czym możesz się przekonać analizując poniższy fragment kodu:
// Objective-C UIView* view = [[UIView alloc] init]; [self.view addSubview:view]; // Swift let view = UIView() self.view.addSubview(view)
Właściwości
Kolejna różnica między językami Objective-C i Swift wiąże się z faktem, że drugi z wymienionych nie rozróżnia zmiennych egzemplarza (tak zwanych ivars) od właściwości. Zmienna egzemplarza jest traktowana jako właściwość.
Deklarowanie właściwości odbywa się w dokładnie taki sam sposób, jak definiowanie zmiennej lub stałej, czyli za pomocą słówkluczowych odpowiednio var
i let
. Jedyna różnica dotyczy kontekstu, w którym zostały zdefiniowane
– tutaj to będzie kontekst klasy.
class Hotel { let rooms = 10 var fullRooms = 0 }
W powyższym przykładzie rooms
to niemodyfikowalna stała, której przypisano wartość 10
, natomiast fullRooms
to zmienna o wartości początkowej wynoszącej 0
. Wartość zmiennej można później zmienić. Regułą jest, że właściwości muszą być zainicjowane
w chwili ich deklarowania. Jedyny wyjątek od tej reguły dotyczy optionals,
czym się zajmiemy w dalszej części artykułu.
Obliczane właściwości
W języku Swift znajdziemy również tak zwane obliczane właściwości. Można je uznać za po prostu inny, nieprzechowujący wartości rodzaj metod typu getter i setter. Jak sama nazwa wskazuje, obliczenie wartości następuje w locie.
Poniżej przedstawiono przykład obliczanej
właściwości. Dla pozostałych przykładów właściwość rooms
została zmieniona na deklarowaną za pomocą słowa kluczowego var
. Powód tej zmiany stanie się jasny w dalszej części
artykułu.
class Hotel { var rooms = 10 var fullRooms = 0 var description: String { get { return "Size of Hotel: \(rooms) rooms capacity:\(fullRooms)/\(rooms)" } } }
Ponieważ właściwość description
jest typu tylko do odczytu i zawiera tylko polecenie return
, więc można pominąć słowo kluczowe get
i nawiasy klamrowe, pozostawiając jedynie polecenie return
. To jest rodzaj skrótu, z którego będziemy korzystać do końca tego
artykułu.
class Hotel { var rooms = 10 var fullRooms = 0 var description: String { return "Size of Hotel: \(rooms) rooms capacity:\(fullRooms)/\(rooms)" } }
Istnieje również możliwość zdefiniowania
obliczanych właściwości, które będą w trybie odczytu i zapisu. Chcemy, aby w naszej przykładowej klasie Hotel
właściwość emptyRooms
pobierała liczbę wolnych pokoi w hotelu. Jednocześnie po przypisaniu
wartości właściwości emptyRooms
powinno następować uaktualnienie fullRooms
. Do tego celu możemy wykorzystać słowo kluczowe set
, jak w poniższym fragmencie kodu:
class Hotel { var rooms = 10 var fullRooms = 0 var description: String { return "Size of Hotel: \(rooms) rooms capacity:\(fullRooms)/\(rooms)" } var emptyRooms :Int { get { return rooms - fullRooms } set { // newValue constant is available here // containing the passed value if(newValue < rooms) { fullRooms = rooms - newValue } else { fullRooms = rooms } } } } let h = Hotel() h.emptyRooms = 3 h.description // Size of Hotel: 10 rooms capacity:7/10
W setterze emptyRooms
stała newValue
jest podawana i przedstawia wartość przekazaną setterowi. Trzeba koniecznie zapamiętać, że obliczane właściwości zawsze są deklarowane jako zmienne za pomocą słowa kluczowego var
, ponieważ ich obliczana wartość może ulec zmianie.
Metody
We wcześniejszej części artykułu zostały omówione
funkcje. Metoda to po prostu funkcja, ale powiązana z typem,
na przykład z klasą. W poniższym fragmencie kodu implementujemy metodę egzemplarza o nazwie bookNumberOfRooms()
w klasie Hotel
, którą utworzyliśmy wcześniej.
class Hotel { var rooms = 10 var fullRooms = 0 var description: String { return "Size of Hotel: \(rooms) rooms capacity:\(fullRooms)/\(rooms)" } var emptyRooms :Int { get { return rooms - fullRooms } set { // newValue constant is available here // containing the passed value if(newValue < rooms) { fullRooms = rooms - newValue } else { fullRooms = rooms } } } func bookNumberOfRooms(room:Int = 1) -> Bool { if(self.emptyRooms>room) { self.fullRooms++; return true } else { return false } } } let h = Hotel() h.emptyRooms = 7 h.description //Size of Hotel: 10 rooms capacity:3/10 h.bookNumberOfRooms(room: 2) // returns true h.description //Size of Hotel: 10 rooms capacity:5/10 h.bookNumberOfRoom() // returns true h.description //Size of Hotel: 10 rooms capacity:6/10
Funkcja inicjalizująca
Domyślna funkcja inicjująca klasę to init()
. W wymienionej funkcji następuje zdefiniowanie wartości początkowych tworzonego egzemplarza.
Na przykład, jeżeli potrzebujemy egzemplarz klasy Hotel
wraz z setką pokoi, wówczas funkcja inicjująca musi przypisać
właściwości rooms
wartość 100
. Pamiętasz, że wcześniej zmieniliśmy w klasie Hotel
typ komponentu rooms
ze stałej na zmienną? Po prostu w klasach potomnych nie można zmieniać
wartości odziedziczonych stałych, zmodyfikowane mogą być tylko dziedziczone
zmienne.
class BigHotel: Hotel { init() { super.init() rooms = 100 } } let bh = BigHotel() println(bh.description); //Size of Hotel: 100 rooms capacity:0/100
Funkcja inicjująca może pobierać parametry. Przykład tego rodzaju rozwiązania przedstawiono w poniższym fragmencie kodu:
class CustomHotel: Hotel { init(size:Int) { super.init() rooms = size } } let c = CustomHotel(size:20) c.description //Size of Hotel: 20 rooms capacity:0/20
Nadpisywanie metod i obliczonych właściwości
To jest jedna z najbardziej interesujących cech
języka Swift. W klasie potomnej można nadpisywać zarówno metody, jak i
obliczone właściwości. W tym celu wykorzystujemy słowo kluczowe override
. Teraz w klasie CustomHotel
nadpiszemy obliczoną właściwość description
:
class CustomHotel: Hotel { init(size:Int) { super.init() rooms = size } override var description:String { return super.description + " Howdy!" } } let c = CustomHotel(size:20) c.description // Size of Hotel: 20 rooms capacity:0/20 Howdy!
Wskutek wprowadzonych zmian, wartością zwracaną przez właściwość description
będzie wynik wywołania metody description
w klasie nadrzędnej i dołączenie do niego ciągu tekstowego Witaj!
.
Najciekawszym aspektem podczas nadpisywania metod i obliczonych właściwości jest słowo kluczowe override
. Kiedy kompilator napotka słowo kluczowe override
, wtedy sprawdza, czy klasa nadrzędna implementuje nadpisywaną metodę. Ponadto kompilator sprawdza, czy właściwości i
metody klasy nie powodują konfliktów z właściwościami lub metodami
znajdującymi się wyżej w drzewie dziedziczenia.
Nie jestem w stanie powiedzieć, ile razy zwykła literówka w nadpisywanej metodzie w Objective-C doprowadzała mnie do szału, ponieważ kod nie działał. W języku Swift, w tego rodzaju sytuacjach kompilator dokładnie wskaże błąd.
Struktury
Struktury są definiowane za pomocą słowa kluczowegostruct
, a ich możliwości znacznie większe niż w językach C i Objective-C. W języku C struktura może zdefiniować jedynie
wartości i wskaźniki. Z kolei struktury w Swifcie są podobne do struktur
znanych z C, ale obsługują także metody i obliczone właściwości.
Wszystko to, co można zrobić za pomocą klasy jest możliwe także do zrobienia za pomocą struktury. Istnieją tylko dwie ważne różnice:
- w przeciwieństwie do klas, struktury nie obsługują dziedziczenia;
- struktury są przekazywane poprzez wartość, podczas gdy klasy przez referencję.
Poniżej pokazano przykłady kilku struktur w języku Swift:
struct Rect { var origin: Point var size: Size var area: Double { return size.width * size.height } func isBiggerThanRect(r:Rect) -> Bool { return (self.area > r.area) } } struct Point { var x = 0 var y = 0 } struct Size { var width = 0 var height = 0 }
4. Optionals
Rozwiązanie problemu
Jeżeli wcześniej programowałeś tylko w Objective-C, wówczas koncepcja optionals będzie dla Ciebie zupełnie nowa. Pozwala na rozwiązanie problemu, przed którym wcześniej lub później stanie każdy programista. Kiedy uzyskujemy dostęp do zmiennej o nieznanej wartości, zwykle zwracamy tak zwaną wartość sentinel wskazującą brak wartości. Poniżej przedstawiono ilustrację tej koncepcji w Objective-C:
NSString* someString = @"ABCDEF"; NSInteger pos = [someString rangeOfString:@"B"].location; // pos = 1
W powyższym fragmencie kodu próbujemy ustalić położenie litery B
w ciągu tekstowym someString
. Jeżeli litera B
zostanie znaleziona, jej położenie będzie przechowywane w zmiennej pos
. Co się stanie w sytuacji, gdy ciąg tekstowy someString
nie zawiera litery B
?
Według dokumentacji Objective-C, wartością zwrotną rangeOfString:
będzie NSRange
wraz z location
o wartości w postaci stałej NSNotFound
. W przypadku rangeOfString:
wartość typu sentinel to NSNotFound
. Wspomniana wartość typu sentinel jest używana do
wskazania, że wartość zwrotna jest niepoprawna.
W Cocoa mamy wiele przykładów użycia tej koncepcji,ale same wartości typu sentinel są różne w zależności od kontekstu: 0
, -1
, NULL
, NSIntegerMax
, INT_MAX
, Nil
itd. Problem programisty wiąże się z koniecznością
zapamiętania, które wartości typu sentinel muszą być stosowane w poszczególnych
kontekstach. Jeżeli programista nie zachowa szczególnej
ostrożności, poprawną wartość może uznać za sentinel i na odwrót. Swift rozwiązuje ten problem za pomocą tak zwanych
optionals. Cytując Briana Laniera: „optionals to jedna wartość
typu sentinel możliwa do stosowania we wszystkich kontekstach”.
Optionals ma dwa stany. Pierwszy to nil
oznaczający, że optional nie ma wartości. Natomiast drugi stan wskazuje na przechowywanie poprawnej wartości. Optionals możesz potraktować jak pakiet wraz ze
wskaźnikiem informującym, czy zawartość pakietu jest prawidłowa.
Sposób użycia
Wszystkie typy w języku Swift mogą być optional. Zdefiniowanie optional odbywa się przez umieszczenie znaku zapytania (?
) po deklaracji, na przykład:
let someInt: Int? // someInt == nil
Przypisanie wartości pakietowi optional następuje w dokładnie taki sam sposób, jak w przypadku stałych i zmiennych:
someInt = 10 // someInt! == 10
Jak wspomniano optional przypomina pakiet. Polecenie let someInt: Int?
powoduje zdefiniowanie pustego pudełka wraz z wartością nil
. Po przypisaniu wartości 10
do optional, pudełko zawiera liczbę całkowitą 10
, a stan przyjmuje postać inna niż nil.
W celu pobrania zawartości optional używamy operator !
. Przed rozpakowaniem optional trzeba się upewnić, że przechowuje poprawną wartość. W przeciwnym razie nastąpi wygenerowanie błędu w
trakcie działania aplikacji. Poniżej pokazano przykład uzyskania dostępu do
wartości przechowywanej w optional:
if ( someInt != nil) { println("someInt: \(someInt!)") } else { println("someInt has no value") } // someInt: 10
Powyższy wzorzec jest na tyle często stosowany wjęzyku Swift, że ten blok kodu można uprościć przez użycie dołączania optional za pomocą słów kluczowych if let
. Spójrz na uaktualnioną wersję kodu:
if let value = someInt { println("someInt: \(value)") } else { println("someInt has no value") }
Optional to jedyny typ, który może przybrać wartość nil
. Stałe lub zmienne nie mogą być zainicjowane z wartością nil
, tej wartości również nie można im przypisać. To jest fragment stosowanej przez język Swift polityki zapewnienia bezpieczeństwa, wszystkie stałe i zmienne inne niż optional muszą mieć wartość.
5. Zarządzanie pamięcią
Jak zapewne pamiętasz, gdy firma Apple wprowadziła mechanizm ARC, do definiowania własności obiektu były używane słowa kluczowe strong
i weak
. W języku Swift również znajduje się ten model zarządzaniawłasnością, ale wprowadzono do niego nowe słowo kluczowe unowned
. Spójrzmy teraz na poszczególne modele własności
obiektu w Swifcie.
strong
W języku Swift domyślnie są stosowane odwołania typu strong. W większości przypadków jesteś właścicielem obiektu, do którego się odwołujesz, a tym samym pozostajesz odpowiedzialny za „utrzymanie go przy życiu”.
Ponieważ tego typu odwołania są stosowane domyślnie, nie ma konieczności wyraźnego stosowania odwołania typu strong. Każde nieoznaczone odwołanie jest odwołaniem typu strong.
weak
Odwołanie typu weak w języku Swift wskazuje na brak odpowiedzialności za „utrzymanie przy życiu” wskazanego obiektu. Tego rodzaju odwołanie najczęściej występuje między obiektami, które nie potrzebują się nawzajem, aby nadal mogły istnieć.
Jednak istnieje jedna ważna kwestia. W języku Swift odwołanie typu weak zawsze musi być zmienną typu optional, ponieważ po usunięciu obiektu zmiennej przypisywana jest wartość nil
. Do zdefiniowania zmiennej jako weak jest używane słowo kluczowe weak
:
weak var view: UIView?
unowned
Odwołanie typu unowned jest nowością dla programistów Objective-C. Tego rodzaju odwołanie oznacza brak odpowiedzialności za utrzymanie obiektu przy życiu, czyli podobnie jak w odwołaniu typu weak.
Różnica między odwołaniami typu weak i unowned polegana tym, że drugie z wymienionych nie będzie miało przypisanej wartości nil
po usunięciu obiektu wskazywanego przez odwołanie. Kolejna ważna różnica w porównaniu do odwołania
typu weak polega
na tym, że odwołania typu unowned są definiowane jako innego typu niż optional.
Odwołanie unowned może być stałą. Wskazywany przez to odwołanie obiekt nie istnieje bez swojego właściciela, a tym samym nigdy nie będzie miał przypisanej wartości nil
. W przypadku tego typu odwołania konieczne jest użycie słowa kluczowego unowned
przed definicją stałej lub zmiennej:
unowned var view: UIView
Podsumowanie
Swift to niesamowity język programowania oferujący potężne możliwości i ogromny potencjał. Tworzenie programów w tym języku sprawia wiele radości, a ponadto nie wymaga dodatkowego kodu, który tworzyliśmy w Objective-C, aby zapewnić bezpieczeństwo kodu.
Gorąco zachęcam Cię do zapoznania się z pozycją The Swift Programming Language, która jest bezpłatnie dostępna w iBooks Store.
Envato Tuts+ tutorials are translated into other languages by our community members—you can be involved too!
Translate this post