iOS 開發者指南:從 Objective-C 到學習 Swift
已發表: 2022-03-112008 年,Apple 宣布並發布了 iPhone SDK 2.0。 這一事件開啟了軟件開發的另一場革命,新一代的開發者誕生了。 他們現在被公認為 iOS 開發者。
其中許多開發人員以前從未使用過 Objective-C,這是 Apple 向他們提出的第一個挑戰。 儘管語法和手動內存管理不熟悉,但它還是取得了巨大的成功,幫助 App Store 填充了數以萬計的應用程序。 Apple 在每個版本中不斷改進 Objective-C,添加塊和文字,通過自動引用計數簡化內存管理,以及許多其他表明現代編程語言的特性。
經過 6 年的改進並致力於 Objective-C,Apple 決定向開發人員提出另一個挑戰。 再一次,iOS 開發者需要學習一門新的編程語言: Swift 。 Swift 移除了不安全的指針管理並引入了強大的新功能,同時保持與 Objective-C 和 C 的交互。
Swift 1.0 已經是一個穩定而強大的開發平台,它肯定會在未來幾年以有趣的方式發展。 現在是開始探索這種新語言的絕佳時機,因為它顯然是 iOS 開發的未來。
本教程的目的是讓 Objective-C 開發人員快速了解 Swift 語言的新特性,幫助您邁出下一步並開始在日常工作中採用 Swift。 我不會花太多時間解釋 Objective-C,我會假設你熟悉 iOS 開發。
嘗試 Swift 與 Objective-C
為了開始探索 Swift,您只需從 App Store 下載 XCode 並創建一個 Playground 進行實驗。 本文中提到的所有示例都是以這種方式完成的。
Apple 的 Swift 主頁是學習 Swift 編程的最佳參考。 你會發現它非常寶貴,在你完全跟上 Swift 開發的速度之前,我相信你會經常回到這裡。
變量和常量
在 Swift 中聲明一個變量是使用var
關鍵字完成的。
var x = 1 var s = "Hello"
您會注意到兩個變量x
和s
的類型不同。 x
是一個整數,而s
是一個字符串。 Swift 是一種類型安全的語言,它會從賦值中推導出變量類型。 如果您希望使您的代碼更具可讀性,您可以選擇註釋變量的類型:
var y: Int y = 2
常量是相似的,但是你使用let
而不是var
來聲明它們。 常量的值不需要在編譯時就知道,但你必須給它賦值一次。
let c1 = 1 // Constant known at compile time var v = arc4random() let c2 = v // Constant known only at run time
顧名思義,它們是不可變的,所以下面的代碼會導致編譯時錯誤。
let c = 1 c = 3 // error
其他類型也可以聲明為常量。 例如,下面的代碼將一個數組聲明為一個常量,如果你試圖修改其中的任何一個元素,Swift 編譯器就會報錯:
var arr2 = [4, 5, 6] arr2[0] = 8 print (arr2) // [8, 5, 6] let arr = [1, 2, 3] a[0] = 5 // error
可選的
常量在聲明時需要初始化,變量在使用前需要初始化。 那麼Objective-C nil
等價物在哪裡呢? Swift 引入了可選值。 可選值可以有一個值或為nil
。 如果您查看以下代碼,您會注意到x
被分配了一個Optional
值2014
。 這意味著 Swift 編譯器知道x
也可能是nil
。
var s = "2014" var x = s.toInt() print(x) // Optional(2014)
如果您對此代碼進行更改並將值"abc"
分配給無法轉換為整數s
,您會注意到x
現在是nil
。
var s = "abc" var x = s.toInt() print(x) // nil
toInt()
函數的返回類型是Int?
,這是一個可選的 Int 。 讓我們嘗試在x
上調用標準函數:
var x = "2014".toInt() print(x.successor()) // error
編譯器發出錯誤信號,因為x
是可選的並且可能是 nil 。 我們必須先測試x
,並確保在實數上調用successor
函數,而不是nil
值:
var x = "2014".toInt() if x != nil { print(x!.successor()) // 2015 }
請注意,我們必須通過附加感嘆號 (!)來解開x
。 當我們確定x
包含一個值時,我們可以訪問它。 否則我們會得到一個運行時錯誤。 我們也可以做 Swift 所稱的可選綁定,將可選變量轉換為非可選變量
let x = "123".toInt() if let y = x { print(y) }
if
語句中的代碼僅在x
有值時才會執行,並將其分配給y
。 請注意,我們不需要解包y
,它的類型不是可選的,因為我們知道x
不是nil
。
查看 Apple 的 Swift 教程,了解更多關於可選項和不錯的特性(如可選鏈)的詳細信息
字符串插值
在 Objective-C 中格式化字符串通常使用stringWithFormat:
方法:
NSString *user = @"Gabriel"; int days = 3; NSString *s = [NSString stringWithFormat:@"posted by %@ (%d days ago)", user, days];
Swift 有一個稱為字符串插值的功能可以做同樣的事情,但它更緊湊且更易於閱讀:
let user = "Gabriel" let days = 3 let s = "posted by \(user) \(days) ago"
您還可以使用表達式:
let width = 2 let height = 3 let s = "Area for square with sides \(width) and \(height) is \(width*height)"
要了解有關 Swift 的字符串插值和其他新功能的更多信息,請訪問此處。
職能
Swift 中的函數定義與 C 不同。示例函數定義如下:
func someFunction(s:String, i: Int) -> Bool { ... // code }
Swift 函數是一流的類型。 這意味著您可以將函數分配給變量,將它們作為參數傳遞給其他函數,或者使它們返回類型:
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
同樣,Swift 推斷出f1
和f2
的類型( String
-> Int
),儘管我們可以明確定義它們:
let f1:String -> Int = stringLength
函數還可以返回其他函數:
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))
可以在此處找到 Swift 中的函數指南。
枚舉
Swift 中的枚舉比 Objective-C 中的強大得多。 作為 Swift 結構,它們可以有方法,並按值傳遞:
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"
與 Objective-C 不同,Swift 枚舉除了整數之外,還可以將字符串、字符或浮點數作為每個成員的值。 方便的toRaw()
方法返回分配給每個成員的值。
枚舉也可以參數化:
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)"
有關枚舉的更多信息,請參見此處。

元組
元組將多個值組合成一個複合值。 元組中的值可以是任何類型,並且不必彼此具有相同的類型。
let person = ("Gabriel", "Kirkpatrick") print(person.0) // Gabriel
您還可以命名單個元組元素:
let person = (first: "Gabriel", last: "Kirkpatrick") print(person.first)
對於需要返回多個值的函數,元組作為返回類型非常方便:
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
與 Objective-C 不同,Swift 支持在 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") }
在第二種情況下,我們不關心數字的實部,所以我們使用_
來匹配任何內容。 您還可以檢查每種情況下的附加條件。 為此,我們需要將模式值綁定到常量:
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)") }
請注意我們如何只需要綁定我們將在比較或 case 語句中使用的值。
要閱讀有關元組的更多信息,請轉到此處。
類和結構
與 Objective-C 不同,Swift 不需要您為自定義類和結構創建單獨的接口和實現文件。 在學習 Swift 時,您將學習在單個文件中定義一個類或結構,並且該類或結構的外部接口會自動提供給其他代碼使用。
定義類
類定義非常簡單:
class Bottle { var volume: Int = 1000 func description() -> String { return "This bottle has \(volume) ml" } } let b = Bottle() print(b.description())
如您所見,聲明和實現在同一個文件中。 Swift 不再使用頭文件和實現文件。 讓我們為示例添加一個標籤:
class Bottle { var volume: Int = 1000 var label:String func description() -> String { return "This bottle of \(label) has \(volume) ml" } }
編譯器會抱怨,因為 label 是一個非可選變量,並且在實例化 Bottle 時不會保存值。 我們需要添加一個初始化器:
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" } }
或者,我們可以對不需要初始化的屬性使用Optional
類型。 在以下示例中,我們將volume
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)" } } }
結構
Swift 語言也有structs
,但它們比 Objective-C 靈活得多。 以下代碼教程定義了一個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)" } }
與 Swift 中的類一樣,結構可以具有方法、屬性、初始化器並符合協議。 類和結構的主要區別在於類是通過引用傳遞的,而結構是通過值傳遞的。
這個例子演示了通過引用傳遞類:
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
如果我們用struct
嘗試類似的情況,你會注意到變量是按值傳遞的:
var s1 = Seat(row: 14, letter:"A") var s2 = s1 s1.letter = "B" print(s1.description()) // 14-B print(s2.description()) // 14-A
我們什麼時候應該使用struct
什麼時候應該使用class
? 與在 Objective-C 和 C 中一樣,當您需要對幾個值進行分組並期望它們被複製而不是引用時,請使用結構。 例如,複數、2D 或 3D 點或 RGB 顏色。
類的實例傳統上稱為對象。 然而,Swift 的類和結構在功能上比其他語言更接近,許多功能可以應用於類或結構類型的實例。 正因為如此,Swift 引用中使用的更通用的術語是instance
,它適用於這兩個中的任何一個。
在這裡學習 Swift 類和結構的基礎知識。
特性
正如我們之前看到的,Swift 中的屬性是在類或結構定義中使用var
關鍵字聲明的。 我們也可以使用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
還要記住,類屬性是強引用的,除非您在它們前面加上weak
關鍵字。 然而,弱非可選屬性有一些微妙之處,因此請閱讀 Apple 的 Swift 指南中的自動引用計數章節。
計算屬性
計算屬性實際上並不存儲值。 相反,它們提供了一個 getter 和一個可選的 setter 來間接檢索和設置其他屬性和值。
以下代碼提供了計算值sign
的示例:
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) } } } }
我們也可以通過實現一個 getter 來定義只讀屬性:
struct SomeNumber { var number:Int var isEven:Bool { get { return number % 2 == 0 } } }
在 Objective-C 中,屬性通常由實例變量支持,由編譯器顯式聲明或自動創建。 另一方面,在 Swift 中,屬性沒有對應的實例變量。 也就是說,不能直接訪問屬性的後備存儲。 假設我們在 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
因為,在 Swift 中,計算屬性沒有後備存儲,我們需要做這樣的事情:
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 } } }
屬性在這裡有更詳細的解釋
待續
在 Swift 中還有許多更重要的新東西需要學習,例如泛型、與 Objective-C 庫的交互、閉包、可選鍊和運算符重載。 單一的教程無法完整地描述一門新語言,但我毫不懷疑關於學習 Swift 編程的更多內容。 但是,我相信這篇快速閱讀將幫助許多沒有時間和學習 Swift 語言細節的 Objective-C 開發人員走上正軌,讓 Swift 鳥把他們帶到一個新的高度。