Przewodnik dla programistów iOS: od Objective-C do nauki Swift

Opublikowany: 2022-03-11

W 2008 roku firma Apple ogłosiła i wypuściła pakiet SDK dla iPhone'a 2.0. To wydarzenie zapoczątkowało kolejną rewolucję w tworzeniu oprogramowania i narodził się nowy rodzaj programistów. Są teraz rozpoznawani jako programiści iOS.

Wielu z tych programistów nigdy wcześniej nie używało Objective-C i było to pierwsze wyzwanie, jakie rzucił im Apple. Pomimo nieznanej składni i ręcznego zarządzania pamięcią, odniósł ogromny sukces, pomagając zapełnić App Store dziesiątkami tysięcy aplikacji. Apple stale ulepszał Objective-C z każdym wydaniem, dodając bloki i literały, uproszczone zarządzanie pamięcią z automatycznym liczeniem referencji i wiele innych funkcji wskazujących na nowoczesny język programowania.

A po 6 latach ulepszania i pracy nad Objective-C, Apple zdecydowało się rzucić deweloperom kolejne wyzwanie. Po raz kolejny programiści iOS będą musieli nauczyć się nowego języka programowania: Swift . Swift usuwa niebezpieczne zarządzanie wskaźnikami i wprowadza nowe zaawansowane funkcje, zachowując interakcję zarówno z Objective-C, jak i C.

Swift 1.0 jest już stabilną i silną platformą programistyczną, która z pewnością będzie ewoluować w interesujący sposób w nadchodzących latach. To doskonały moment na rozpoczęcie odkrywania tego nowego języka, ponieważ jest to oczywiście przyszłość rozwoju iOS.

Celem tego samouczka jest umożliwienie programistom Objective-C szybkiego przeglądu nowych funkcji języka Swift, co pomoże Ci zrobić kolejny krok i zacząć adoptować Swift w codziennej pracy. Nie będę spędzał zbyt wiele czasu na wyjaśnianiu Objective-C i zakładam, że znasz się na rozwoju iOS.

Swift zmienił grę dla deweloperów Objective-C iOS.

Wypróbowanie Swift kontra Cel-C

Aby rozpocząć eksplorację Swift, wystarczy pobrać XCode z App Store i stworzyć plac zabaw do eksperymentowania. Wszystkie przykłady wymienione w tym artykule są wykonane w ten sposób.

Strona główna Apple Swift jest najlepszym punktem odniesienia do nauki programowania w języku Swift. Przekonasz się, że jest to bezcenne i dopóki nie będziesz w pełni na bieżąco z rozwojem Swift, wierzę, że będziesz tu często wracać.

Zmienne i stałe

Deklarowanie zmiennej w Swift odbywa się za pomocą słowa kluczowego var .

 var x = 1 var s = "Hello"

Zauważysz, że dwie zmienne x i s są różnych typów. x to liczba całkowita, a s to łańcuch. Swift jest językiem bezpiecznym dla typów i będzie wyprowadzał typy zmiennych z przypisanej wartości. Jeśli chcesz, aby Twój kod był bardziej czytelny, możesz opcjonalnie opisać typ zmiennej:

 var y: Int y = 2

Stałe są podobne, ale deklarujesz je za pomocą let zamiast var . Wartość stałej nie musi być znana w czasie kompilacji, ale musisz przypisać jej wartość dokładnie raz.

 let c1 = 1 // Constant known at compile time var v = arc4random() let c2 = v // Constant known only at run time

Jak sugeruje ich nazwa, są one niezmienne, więc poniższy kod spowoduje błąd w czasie kompilacji.

 let c = 1 c = 3 // error

Inne typy można również zadeklarować jako stałe. Na przykład poniższy kod deklaruje tablicę jako stałą, a jeśli spróbujesz zmodyfikować dowolny z elementów, kompilator Swift zgłosi błąd:

 var arr2 = [4, 5, 6] arr2[0] = 8 print (arr2) // [8, 5, 6] let arr = [1, 2, 3] a[0] = 5 // error

Opcjonalne

Stałe należy zainicjować podczas ich deklarowania, a zmienne należy zainicjować przed użyciem. Więc gdzie jest odpowiednik nil Objective-C? Swift wprowadza opcjonalne wartości . Wartości opcjonalne mogą mieć wartość lub być nil . Jeśli spojrzysz na poniższy kod, zauważysz, że x przypisano wartość Optional 2014 . Oznacza to, że kompilator Swift zdawał sobie sprawę, że x może również być nil .

 var s = "2014" var x = s.toInt() print(x) // Optional(2014)

Jeśli dokonasz zmiany w tym kodzie i przypiszesz wartość "abc" do s , której nie można przekonwertować na liczbę całkowitą, zauważysz, że x jest teraz nil .

 var s = "abc" var x = s.toInt() print(x) // nil

Zwracanym typem funkcji toInt() jest Int? , który jest opcjonalnym Int . Spróbujmy wywołać standardową funkcję na x :

 var x = "2014".toInt() print(x.successor()) // error

Kompilator sygnalizuje błąd, ponieważ x jest opcjonalny i potencjalnie może być nil . Najpierw musimy przetestować x i upewnić się, że funkcja successor jest wywoływana na liczbie rzeczywistej, a nie na wartości nil :

 var x = "2014".toInt() if x != nil { print(x!.successor()) // 2015 }

Zauważ, że musimy rozpakować x , dołączając znak wykrzyknika (!) . Gdy jesteśmy pewni, że x zawiera wartość, możemy uzyskać do niej dostęp. W przeciwnym razie otrzymamy błąd w czasie wykonywania. Możemy również zrobić to, co Swift nazywa wiązaniem opcjonalnym , przekształcając zmienną opcjonalną w zmienną nieopcjonalną

 let x = "123".toInt() if let y = x { print(y) }

Kod w instrukcji if zostanie wykonany tylko wtedy, gdy x ma wartość i przypisze ją do y . Zauważ, że nie musimy rozpakowywać y , jego typ nie jest opcjonalny, ponieważ wiemy, że x to nie nil .

Sprawdź samouczek Apple Swift, aby przeczytać więcej szczegółów na temat opcji i ciekawych funkcji, takich jak opcjonalne tworzenie łańcuchów

Interpolacja ciągów

W Objective-C formatowanie ciągów jest zwykle wykonywane za pomocą metody stringWithFormat: ::

 NSString *user = @"Gabriel"; int days = 3; NSString *s = [NSString stringWithFormat:@"posted by %@ (%d days ago)", user, days];

Swift ma funkcję zwaną interpolacją ciągów , która robi to samo, ale jest bardziej zwarta i łatwiejsza do odczytania:

 let user = "Gabriel" let days = 3 let s = "posted by \(user) \(days) ago"

Możesz także użyć wyrażeń:

 let width = 2 let height = 3 let s = "Area for square with sides \(width) and \(height) is \(width*height)"

Aby dowiedzieć się więcej o interpolacji ciągów Swift i innych nowych funkcjach, przejdź tutaj.

Funkcje

Definicja funkcji w Swift różni się od C. Przykładowa definicja funkcji jest poniżej:

 func someFunction(s:String, i: Int) -> Bool { ... // code }

Funkcje Swift to pierwszorzędne typy . Oznacza to, że możesz przypisać funkcje do zmiennych, przekazać je jako parametry do innych funkcji lub sprawić, by zwracały typy:

 func stringLength(s:String) -> Int { return countElements(s) } func stringValue(s:String) -> Int { if let x = s.toInt() { return x } return 0 } func doSomething(f:String -> Int, s:String) -> Int { return f(s).successor() } let f1 = stringLength let f2 = stringValue doSomething(f1, "123") // 4 doSomething(f2, "123") // 124

Ponownie, Swift wyprowadza typy f1 i f2 ( String -> Int ), chociaż mogliśmy je zdefiniować jawnie:

 let f1:String -> Int = stringLength

Funkcje mogą również zwracać inne funkcje:

 func compareGreaterThan(a: Int, b: Int) -> Bool { return a > b } func compareLessThan(a: Int, b: Int) -> Bool { return a < b } func comparator(greaterThan:Bool) -> (Int, Int) -> Bool { if greaterThan { return compareGreaterThan } else { return compareLessThan } } let f = comparator(true) println(f(5, 9))

Przewodnik po funkcjach w Swift można znaleźć tutaj.

Wyliczenia

Wyliczenia w Swift są znacznie potężniejsze niż w Objective-C. Jako struktury Swift mogą mieć metody i są przekazywane przez wartość:

 enum MobileDevice : String { case iPhone = "iPhone", Andro, WP8 = "Windows Phone8", BB = "BlackBerry" func name() -> String { return self.toRaw() } } let m = MobileDevice.Android print(m.name()) // "Android"

W przeciwieństwie do celu C, wyliczenia Swift mogą przypisywać ciągi, znaki lub zmiennoprzecinkowe jako wartości dla każdego elementu członkowskiego, oprócz liczb całkowitych. Wygodna toRaw() zwraca wartość przypisaną do każdego członka.

Wyliczenia można również sparametryzować:

 enum Location { case Address(street:String, city:String) case LatLon(lat:Float, lon:Float) func description() -> String { switch self { case let .Address(street, city): return street + ", " + city case let .LatLon(lat, lon): return "(\(lat), \(lon))" } } } let loc1 = Location.Address(street: "2070 Fell St", city: "San Francisco") let loc2 = Location.LatLon(lat: 23.117, lon: 45.899) print(loc1.description()) // "2070 Fell St, San Francisco" print(loc2.description()) // "(23.117, 45.988)"

Więcej informacji o wyliczeniach można znaleźć tutaj.

Krotki

Krotki grupują wiele wartości w jedną wartość złożoną. Wartości w krotce mogą być dowolnego typu i nie muszą być tego samego typu.

 let person = ("Gabriel", "Kirkpatrick") print(person.0) // Gabriel

Możesz również nazwać poszczególne elementy krotki:

 let person = (first: "Gabriel", last: "Kirkpatrick") print(person.first)

Krotki są niezwykle wygodne jako typy zwracane dla funkcji, które muszą zwracać więcej niż jedną wartość:

 func intDivision(a: Int, b: Int) -> (quotient: Int, remainder: Int) { return (a/b, a%b) } print(intDivision(11, 3)) // (3, 2) let result = intDivision(15, 4) print(result.remainder) // 3

W przeciwieństwie do Objective-C, Swift obsługuje dopasowanie wzorców w instrukcji switch:

 let complex = (2.0, 1.1) // real and imaginary parts switch complex { case (0, 0): println("Number is zero") case (_, 0): println("Number is real") default: println("Number is imaginary") }

W drugim przypadku nie dbamy o rzeczywistą część liczby, więc używamy _ , aby dopasować cokolwiek. W każdym przypadku możesz również sprawdzić dodatkowe warunki. W tym celu musimy powiązać wartości wzorca ze stałymi:

 let complex = (2.0, 1.1) switch complex { case (0, 0): println("Number is zero") case (let a, 0) where a > 0: println("Number is real and positive") case (let a, 0) where a < 0: println("Number is real and negative") case (0, let b) where b != 0: println("Number has only imaginary part") case let (a, b): println("Number is imaginary with distance \(a*a + b*b)") }

Zwróć uwagę, że musimy powiązać tylko wartości, których będziemy używać w porównaniu lub w instrukcji case.

Aby przeczytać więcej o krotkach, przejdź tutaj.

Klasy i struktury

W przeciwieństwie do Objective-C, Swift nie wymaga tworzenia oddzielnych plików interfejsu i implementacji dla niestandardowych klas i struktur. Podczas nauki języka Swift nauczysz się definiować klasę lub strukturę w jednym pliku, a interfejs zewnętrzny do tej klasy lub struktury jest automatycznie udostępniany do użycia przez inny kod.

Definiowanie klas

Definicje klas są bardzo proste:

 class Bottle { var volume: Int = 1000 func description() -> String { return "This bottle has \(volume) ml" } } let b = Bottle() print(b.description())

Jak widać, deklaracja i implementacja znajdują się w tym samym pliku . Swift nie używa już plików nagłówka i implementacji. Dodajmy etykietę do naszego przykładu:

 class Bottle { var volume: Int = 1000 var label:String func description() -> String { return "This bottle of \(label) has \(volume) ml" } }

Kompilator będzie narzekał, ponieważ etykieta jest zmienną nieopcjonalną i nie będzie przechowywać wartości podczas tworzenia instancji Butelki. Musimy dodać inicjator:

 class Bottle { var volume: Int = 1000 var label:String init(label:String) { self.label = label } func description() -> String { return "This bottle of \(label) has \(volume) ml" } }

Lub możemy użyć typu Optional dla właściwości, której nie należy inicjować. W poniższym przykładzie określiliśmy volume jako Optional Integer :

 class Bottle { var volume: Int? var label:String init(label:String) { self.label = label } func description() -> String { if self.volume != nil { return "This bottle of \(label) has \(volume!) ml" } else { return "A bootle of \(label)" } } }

Struktury

Język Swift również posiada structs , ale są one znacznie bardziej elastyczne niż w Objective-C. Poniższy samouczek kodu definiuje struct :

 struct Seat { var row: Int var letter:String init (row: Int, letter:String) { self.row = row self.letter = letter } func description() -> String { return "\(row)-\(letter)" } }

Podobnie jak klasy w języku Swift, struktury mogą mieć metody, właściwości, inicjatory i być zgodne z protokołami. Główna różnica między klasami a strukturami polega na tym, że klasy są przekazywane przez referencję, podczas gdy struktury są przekazywane przez value .

Ten przykład ilustruje przekazywanie klas przez odniesienie:

 let b = Bottle() print(b.description()) // "b" bottle has 1000 ml var b2 = b b.volume = 750 print(b2.description()) // "b" and "b2" bottles have 750 ml

Jeśli spróbujemy podobny przypadek z struct , zauważysz, że zmienne są przekazywane przez wartość:

 var s1 = Seat(row: 14, letter:"A") var s2 = s1 s1.letter = "B" print(s1.description()) // 14-B print(s2.description()) // 14-A

Kiedy powinniśmy użyć struct a kiedy class ? Podobnie jak w Objective-C i C, użyj struktur, gdy musisz pogrupować kilka wartości i oczekiwać, że będą one kopiowane, a nie przywoływane. Na przykład liczby zespolone, punkty 2D lub 3D lub kolory RGB.

Instancja klasy jest tradycyjnie nazywana obiektem. Jednak klasy i struktury Swift są znacznie bliższe funkcjonalności niż w innych językach, a wiele funkcji można zastosować do wystąpień klasy lub typu struktury. Z tego powodu bardziej ogólnym terminem używanym w Swift reference jest instance , które odnosi się do każdego z tych dwóch.

Poznaj podstawy klas i struktur Swift tutaj.

Nieruchomości

Jak widzieliśmy wcześniej, właściwości w języku Swift są deklarowane za pomocą słowa kluczowego var wewnątrz definicji klasy lub struktury. Możemy również zadeklarować stałe za pomocą instrukcji let .

 struct FixedPointNumber { var digits: Int let decimals: Int } var n = FixedPointNumber(digits: 12345, decimals: 2) n.digits = 4567 // ok n.decimals = 3 // error, decimals is a constant

Pamiętaj też, że właściwości klasy są silnie przywoływane, chyba że poprzedzisz je słowem kluczowym weak . Jednak istnieją pewne subtelności ze słabymi, nieopcjonalnymi właściwościami, więc przeczytaj rozdział dotyczący automatycznego liczenia referencji w przewodniku Apple Swift.

Obliczone właściwości

Obliczone właściwości w rzeczywistości nie przechowują wartości. Zamiast tego udostępniają metodę pobierającą i opcjonalny ustawiający do pobierania i ustawiania innych właściwości i wartości w sposób pośredni.

Poniższy kod zawiera przykładowy sign wartości obliczonej:

 enum Sign { case Positive case Negative } struct SomeNumber { var number:Int var sign:Sign { get { if number < 0 { return Sign.Negative } else { return Sign.Positive } } set (newSign) { if (newSign == Sign.Negative) { self.number = -abs(self.number) } else { self.number = abs(self.number) } } } }

Możemy również zdefiniować właściwości tylko do odczytu, po prostu implementując getter:

 struct SomeNumber { var number:Int var isEven:Bool { get { return number % 2 == 0 } } }

W Objective-C właściwości są zwykle wspierane przez zmienną wystąpienia, zadeklarowaną jawnie lub automatycznie utworzoną przez kompilator. Z drugiej strony w języku Swift właściwość nie ma odpowiadającej zmiennej instancji . Oznacza to, że nie można uzyskać bezpośredniego dostępu do magazynu zapasowego właściwości. Załóżmy, że mamy to w Objective-C

 // .h @interface OnlyInitialString : NSObject @property(strong) NSString *string; @end // .m @implementation OnlyInitialString - (void)setString:(NSString *newString) { if (newString.length > 0) { _string = [newString substringToIndex:1]; } else { _string = @""; } } @end

Ponieważ w Swift przeliczane właściwości nie mają zapasowego magazynu, musimy zrobić coś takiego:

 class OnlyInitialString { var initial:String = "" var string:String { set (newString) { if countElements(newString) > 0 { self.initial = newString.substringToIndex(advance(newString.startIndex, 1)) } else { self.initial = "" } } get { return self.initial } } }

Właściwości są wyjaśnione bardziej szczegółowo tutaj

Ciąg dalszy nastąpi

Istnieje wiele ważniejszych nowych rzeczy do nauczenia się w Swift, takich jak Generics, interakcja z bibliotekami Objective-C, domknięcia, opcjonalne łańcuchy i przeciążanie operatorów. Pojedynczy samouczek nie może dokładnie opisać nowego języka, ale nie mam wątpliwości, że o nauce programowania w języku Swift zostanie napisane znacznie więcej. Wierzę jednak, że ta krótka lektura pomoże wielu programistom Objective-C, którym nie udało się znaleźć czasu i poznać szczegółów języka Swift, na właściwe tory i pozwolić ptakowi Swift wznieść się na nowe wyżyny.