# [X分鐘速成Y](http://learnxinyminutes.com/)
## 其中 Y=swift
源代碼下載:?[learnswift-cn.swift](http://learnxinyminutes.com/docs/files/learnswift-cn.swift)
Swift 是 Apple 開發的用于 iOS 和 OS X 開發的編程語言。Swift 于2014年 Apple WWDC (全球開發者大會)中被引入,用以與 Objective-C 共存,同時對錯誤代碼更具彈性。Swift 由 Xcode 6 beta 中包含的 LLVM 編譯器編譯。
Swift 的官方語言教程?[Swift Programming Language](https://itunes.apple.com/us/book/swift-programming-language/id881256329)?可以從 iBooks 免費下載.
亦可參閱:Apple’s?[getting started guide](https://developer.apple.com/library/prerelease/ios/referencelibrary/GettingStarted/RoadMapiOS/index.html)?——一個完整的Swift 教程
~~~
// 導入外部模塊
import UIKit
//
// MARK: 基礎
//
// XCODE 支持給注釋代碼作標記,這些標記會列在 XCODE 的跳轉欄里,支持的標記為
// MARK: 普通標記
// TODO: TODO 標記
// FIXME: FIXME 標記
println("Hello, world")
// 變量 (var) 的值設置后可以隨意改變
// 常量 (let) 的值設置后不能改變
var myVariable = 42
let ?πΩ = "value" // 可以支持 unicode 變量名
let π = 3.1415926
let myConstant = 3.1415926
let explicitDouble: Double = 70 // 明確指定變量類型為 Double ,否則編譯器將自動推斷變量類型
let weak = "keyword"; let override = "another keyword" // 語句之間可以用分號隔開,語句未尾不需要分號
let intValue = 0007 // 7
let largeIntValue = 77_000 // 77000
let label = "some text " + String(myVariable) // 類型轉換
let piText = "Pi = \(π), Pi 2 = \(π * 2)" // 格式化字符串
// 條件編譯
// 使用 -D 定義編譯開關
#if false
println("Not printed")
let buildValue = 3
#else
let buildValue = 7
#endif
println("Build value: \(buildValue)") // Build value: 7
/*
Optionals 是 Swift 的新特性,它允許你存儲兩種狀態的值給 Optional 變量:有效值或 None
Swift 要求所有的 Optinal 屬性都必須有明確的值,如果為空,則必須明確設定為 nil
Optional<T> 是個枚舉類型
*/
var someOptionalString: String? = "optional" // 可以是 nil
// 下面的語句和上面完全等價,上面的寫法更推薦,因為它更簡潔,問號 (?) 是 Swift 提供的語法糖
var someOptionalString2: Optional<String> = "optional"
if someOptionalString != nil {
// 變量不為空
if someOptionalString!.hasPrefix("opt") {
println("has the prefix")
}
let empty = someOptionalString?.isEmpty
}
someOptionalString = nil
// 顯式解包 optional 變量
var unwrappedString: String! = "Value is expected."
// 下面語句和上面完全等價,感嘆號 (!) 是個后綴運算符,這也是個語法糖
var unwrappedString2: ImplicitlyUnwrappedOptional<String> = "Value is expected."
if let someOptionalStringConstant = someOptionalString {
// 由于變量 someOptinalString 有值,不為空,所以 if 條件為真
if !someOptionalStringConstant.hasPrefix("ok") {
// does not have the prefix
}
}
// Swift 支持可保存任何數據類型的變量
// AnyObject == id
// 和 Objective-C `id` 不一樣, AnyObject 可以保存任何類型的值 (Class, Int, struct, 等)
var anyObjectVar: AnyObject = 7
anyObjectVar = "Changed value to a string, not good practice, but possible."
/*
這里是注釋
/*
支持嵌套的注釋
*/
*/
//
// Mark: 數組與字典(關聯數組)
//
/*
Array 和 Dictionary 是結構體,不是類,他們作為函數參數時,是用值傳遞而不是指針傳遞。
可以用 `var` 和 `let` 來定義變量和常量。
*/
// Array
var shoppingList = ["catfish", "water", "lemons"]
shoppingList[1] = "bottle of water"
let emptyArray = [String]() // 使用 let 定義常量,此時 emptyArray 數組不能添加或刪除內容
let emptyArray2 = Array<String>() // 與上一語句等價,上一語句更常用
var emptyMutableArray = [String]() // 使用 var 定義變量,可以向 emptyMutableArray 添加數組元素
// 字典
var occupations = [
"Malcolm": "Captain",
"kaylee": "Mechanic"
]
occupations["Jayne"] = "Public Relations" // 修改字典,如果 key 不存在,自動添加一個字典元素
let emptyDictionary = [String: Float]() // 使用 let 定義字典常量,字典常量不能修改里面的值
let emptyDictionary2 = Dictionary<String, Float>() // 與上一語句類型等價,上一語句更常用
var emptyMutableDictionary = [String: Float]() // 使用 var 定義字典變量
//
// MARK: 控制流
//
// 數組的 for 循環
let myArray = [1, 1, 2, 3, 5]
for value in myArray {
if value == 1 {
println("One!")
} else {
println("Not one!")
}
}
// 字典的 for 循環
var dict = ["one": 1, "two": 2]
for (key, value) in dict {
println("\(key): \(value)")
}
// 區間的 loop 循環:其中 `...` 表示閉環區間,即[-1, 3];`..<` 表示半開閉區間,即[-1,3)
for i in -1...shoppingList.count {
println(i)
}
shoppingList[1...2] = ["steak", "peacons"]
// 可以使用 `..<` 來去掉最后一個元素
// while 循環
var i = 1
while i < 1000 {
i *= 2
}
// do-while 循環
do {
println("hello")
} while 1 == 2
// Switch 語句
// Swift 里的 Switch 語句功能異常強大,結合枚舉類型,可以實現非常簡潔的代碼,可以把 switch 語句想象成 `if` 的語法糖
// 它支持字符串,類實例或原生數據類型 (Int, Double, etc)
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."
case let localScopeValue where localScopeValue.hasSuffix("pepper"):
let vegetableComment = "Is it a spicy \(localScopeValue)?"
default: // 在 Swift 里,switch 語句的 case 必須處理所有可能的情況,如果 case 無法全部處理,則必須包含 default語句
let vegetableComment = "Everything tastes good in soup."
}
//
// MARK: 函數
//
// 函數是一個 first-class 類型,他們可以嵌套,可以作為函數參數傳遞
// 函數文檔可使用 reStructedText 格式直接寫在函數的頭部
/**
A greet operation
- A bullet in docs
- Another bullet in the docs
:param: name A name
:param: day A day
:returns: A string containing the name and day value.
*/
func greet(name: String, day: String) -> String {
return "Hello \(name), today is \(day)."
}
greet("Bob", "Tuesday")
// 函數參數前帶 `#` 表示外部參數名和內部參數名使用同一個名稱。
// 第二個參數表示外部參數名使用 `externalParamName` ,內部參數名使用 `localParamName`
func greet2(#requiredName: String, externalParamName localParamName: String) -> String {
return "Hello \(requiredName), the day is \(localParamName)"
}
greet2(requiredName:"John", externalParamName: "Sunday") // 調用時,使用命名參數來指定參數的值
// 函數可以通過元組 (tuple) 返回多個值
func getGasPrices() -> (Double, Double, Double) {
return (3.59, 3.69, 3.79)
}
let pricesTuple = getGasPrices()
let price = pricesTuple.2 // 3.79
// 通過下劃線 (_) 來忽略不關心的值
let (_, price1, _) = pricesTuple // price1 == 3.69
println(price1 == pricesTuple.1) // true
println("Gas price: \(price)")
// 可變參數
func setup(numbers: Int...) {
// 可變參數是個數組
let number = numbers[0]
let argCount = numbers.count
}
// 函數變量以及函數作為返回值返回
func makeIncrementer() -> (Int -> Int) {
func addOne(number: Int) -> Int {
return 1 + number
}
return addOne
}
var increment = makeIncrementer()
increment(7)
// 強制進行指針傳遞 (引用傳遞),使用 `inout` 關鍵字修飾函數參數
func swapTwoInts(inout a: Int, inout b: Int) {
let tempA = a
a = b
b = tempA
}
var someIntA = 7
var someIntB = 3
swapTwoInts(&someIntA, &someIntB)
println(someIntB) // 7
//
// MARK: 閉包
//
var numbers = [1, 2, 6]
// 函數是閉包的一個特例
// 閉包實例
// `->` 分隔了閉包的參數和返回值
// `in` 分隔了閉包頭 (包括參數及返回值) 和閉包體
// 下面例子中,`map` 的參數是一個函數類型,它的功能是把數組里的元素作為參數,逐個調用 `map` 參數傳遞進來的函數。
numbers.map({
(number: Int) -> Int in
let result = 3 * number
return result
})
// 當閉包的參數類型和返回值都是己知的情況下,且只有一個語句作為其返回值時,我們可以簡化閉包的寫法
numbers = numbers.map({ number in 3 * number })
// 我們也可以使用 $0, $1 來指代第 1 個,第 2 個參數,上面的語句最終可簡寫為如下形式
// numbers = numbers.map({ $0 * 3 })
print(numbers) // [3, 6, 18]
// 簡潔的閉包
numbers = sorted(numbers) { $0 > $1 }
// 函數的最后一個參數可以放在括號之外,上面的語句是這個語句的簡寫形式
// numbers = sorted(numbers, { $0 > $1 })
print(numbers) // [18, 6, 3]
// 超級簡潔的閉包,因為 `<` 是個操作符函數
numbers = sorted(numbers, < )
print(numbers) // [3, 6, 18]
//
// MARK: 結構體
//
// 結構體和類非常類似,可以有屬性和方法
struct NamesTable {
let names = [String]()
// 自定義下標運算符
subscript(index: Int) -> String {
return names[index]
}
}
// 結構體有一個自動生成的隱含的命名構造函數
let namesTable = NamesTable(names: ["Me", "Them"])
let name = namesTable[1]
println("Name is \(name)") // Name is Them
//
// MARK: 類
//
// 類和結構體的有三個訪問控制級別,他們分別是 internal (默認), public, private
// internal: 模塊內部可以訪問
// public: 其他模塊可以訪問
// private: 只有定義這個類或結構體的源文件才能訪問
public class Shape {
public func getArea() -> Int {
return 0;
}
}
// 類的所有方法和屬性都是 public 的
// 如果你只是需要把數據保存在一個結構化的實例里面,應該用結構體
internal class Rect: Shape {
// 值屬性 (Stored properties)
var sideLength: Int = 1
// 計算屬性 (Computed properties)
private var perimeter: Int {
get {
return 4 * sideLength
}
set {
// `newValue` 是個隱含的變量,它表示將要設置進來的新值
sideLength = newValue / 4
}
}
// 延時加載的屬性,只有這個屬性第一次被引用時才進行初始化,而不是定義時就初始化
// subShape 值為 nil ,直到 subShape 第一次被引用時才初始化為一個 Rect 實例
lazy var subShape = Rect(sideLength: 4)
// 監控屬性值的變化。
// 當我們需要在屬性值改變時做一些事情,可以使用 `willSet` 和 `didSet` 來設置監控函數
// `willSet`: 值改變之前被調用
// `didSet`: 值改變之后被調用
var identifier: String = "defaultID" {
// `willSet` 的參數是即將設置的新值,參數名可以指定,如果沒有指定,就是 `newValue`
willSet(someIdentifier) {
println(someIdentifier)
}
// `didSet` 的參數是已經被覆蓋掉的舊的值,參數名也可以指定,如果沒有指定,就是 `oldValue`
didSet {
println(oldValue)
}
}
// 命名構造函數 (designated inits),它必須初始化所有的成員變量,
// 然后調用父類的命名構造函數繼續初始化父類的所有變量。
init(sideLength: Int) {
self.sideLength = sideLength
// 必須顯式地在構造函數最后調用父類的構造函數 super.init
super.init()
}
func shrink() {
if sideLength > 0 {
--sideLength
}
}
// 函數重載使用 override 關鍵字
override func getArea() -> Int {
return sideLength * sideLength
}
}
// 類 `Square` 從 `Rect` 繼承
class Square: Rect {
// 便捷構造函數 (convenience inits) 是調用自己的命名構造函數 (designated inits) 的構造函數
// Square 自動繼承了父類的命名構造函數
convenience init() {
self.init(sideLength: 5)
}
// 關于構造函數的繼承,有以下幾個規則:
// 1\. 如果你沒有實現任何命名構造函數,那么你就繼承了父類的所有命名構造函數
// 2\. 如果你重載了父類的所有命名構造函數,那么你就自動繼承了所有的父類快捷構造函數
// 3\. 如果你沒有實現任何構造函數,那么你繼承了父類的所有構造函數,包括命名構造函數和便捷構造函數
}
var mySquare = Square()
println(mySquare.getArea()) // 25
mySquare.shrink()
println(mySquare.sideLength) // 4
// 類型轉換
let aShape = mySquare as Shape
// 使用三個等號來比較是不是同一個實例
if mySquare === aShape {
println("Yep, it's mySquare")
}
class Circle: Shape {
var radius: Int
override func getArea() -> Int {
return 3 * radius * radius
}
// optional 構造函數,可能會返回 nil
init?(radius: Int) {
self.radius = radius
super.init()
if radius <= 0 {
return nil
}
}
}
// 根據 Swift 類型推斷,myCircle 是 Optional<Circle> 類型的變量
var myCircle = Circle(radius: 1)
println(myCircle?.getArea()) // Optional(3)
println(myCircle!.getArea()) // 3
var myEmptyCircle = Circle(radius: -1)
println(myEmptyCircle?.getArea()) // "nil"
if let circle = myEmptyCircle {
// 此語句不會輸出,因為 myEmptyCircle 變量值為 nil
println("circle is not nil")
}
//
// MARK: 枚舉
//
// 枚舉可以像類一樣,擁有方法
enum Suit {
case Spades, Hearts, Diamonds, Clubs
func getIcon() -> String {
switch self {
case .Spades: return "?"
case .Hearts: return "?"
case .Diamonds: return "?"
case .Clubs: return "?"
}
}
}
// 當變量類型明確指定為某個枚舉類型時,賦值時可以省略枚舉類型
var suitValue: Suit = .Hearts
// 非整型的枚舉類型需要在定義時賦值
enum BookName: String {
case John = "John"
case Luke = "Luke"
}
println("Name: \(BookName.John.rawValue)")
// 與特定數據類型關聯的枚舉
enum Furniture {
// 和 Int 型數據關聯的枚舉記錄
case Desk(height: Int)
// 和 String, Int 關聯的枚舉記錄
case Chair(brand: String, height: Int)
func description() -> String {
switch self {
case .Desk(let height):
return "Desk with \(height) cm"
case .Chair(let brand, let height):
return "Chair of \(brand) with \(height) cm"
}
}
}
var desk: Furniture = .Desk(height: 80)
println(desk.description()) // "Desk with 80 cm"
var chair = Furniture.Chair(brand: "Foo", height: 40)
println(chair.description()) // "Chair of Foo with 40 cm"
//
// MARK: 協議
// 與 Java 的 interface 類似
//
// 協議可以讓遵循同一協議的類型實例擁有相同的屬性,方法,類方法,操作符或下標運算符等
// 下面代碼定義一個協議,這個協議包含一個名為 enabled 的計算屬性且包含 buildShape 方法
protocol ShapeGenerator {
var enabled: Bool { get set }
func buildShape() -> Shape
}
// 協議聲明時可以添加 @objc 前綴,添加 @objc 前綴后,
// 可以使用 is, as, as? 等來檢查協議兼容性
// 需要注意,添加 @objc 前綴后,協議就只能被類來實現,
// 結構體和枚舉不能實現加了 @objc 的前綴
// 只有添加了 @objc 前綴的協議才能聲明 optional 方法
// 一個類實現一個帶 optional 方法的協議時,可以實現或不實現這個方法
// optional 方法可以使用 optional 規則來調用
@objc protocol TransformShape {
optional func reshaped()
optional func canReshape() -> Bool
}
class MyShape: Rect {
var delegate: TransformShape?
func grow() {
sideLength += 2
// 在 optional 屬性,方法或下標運算符后面加一個問號,可以優雅地忽略 nil 值,返回 nil。
// 這樣就不會引起運行時錯誤 (runtime error)
if let allow = self.delegate?.canReshape?() {
// 注意語句中的問號
self.delegate?.reshaped?()
}
}
}
//
// MARK: 其它
//
// 擴展: 給一個已經存在的數據類型添加功能
// 給 Square 類添加 `Printable` 協議的實現,現在其支持 `Printable` 協議
extension Square: Printable {
var description: String {
return "Area: \(self.getArea()) - ID: \(self.identifier)"
}
}
println("Square: \(mySquare)") // Area: 16 - ID: defaultID
// 也可以給系統內置類型添加功能支持
extension Int {
var customProperty: String {
return "This is \(self)"
}
func multiplyBy(num: Int) -> Int {
return num * self
}
}
println(7.customProperty) // "This is 7"
println(14.multiplyBy(3)) // 42
// 泛型: 和 Java 及 C# 的泛型類似,使用 `where` 關鍵字來限制類型。
// 如果只有一個類型限制,可以省略 `where` 關鍵字
func findIndex<T: Equatable>(array: [T], valueToFind: T) -> Int? {
for (index, value) in enumerate(array) {
if value == valueToFind {
return index
}
}
return nil
}
let foundAtIndex = findIndex([1, 2, 3, 4], 3)
println(foundAtIndex == 2) // true
// 自定義運算符:
// 自定義運算符可以以下面的字符打頭:
// / = - + * % < > ! & | ^ . ~
// 甚至是 Unicode 的數學運算符等
prefix operator !!! {}
// 定義一個前綴運算符,使矩形的邊長放大三倍
prefix func !!! (inout shape: Square) -> Square {
shape.sideLength *= 3
return shape
}
// 當前值
println(mySquare.sideLength) // 4
// 使用自定義的 !!! 運算符來把矩形邊長放大三倍
!!!mySquare
println(mySquare.sideLength) // 12
~~~