源代碼下載:?learnswift-cn.swift
Swift 是 Apple 開發(fā)的用于 iOS 和 OS X 開發(fā)的編程語言。Swift 于2014年 Apple WWDC (全球開發(fā)者大會)中被引入,用以與 Objective-C 共存,同時對錯誤代碼更具彈性。Swift 由 Xcode 6 beta 中包含的 LLVM 編譯器編譯。
Swift 的官方語言教程?Swift Programming Language?可以從 iBooks 免費下載.
亦可參閱:Apple’s?getting started guide?——一個完整的Swift 教程
// 導(dǎo)入外部模塊
import UIKit
//
// MARK: 基礎(chǔ)
//
// XCODE 支持給注釋代碼作標(biāo)記,這些標(biāo)記會列在 XCODE 的跳轉(zhuǎn)欄里,支持的標(biāo)記為
// MARK: 普通標(biāo)記
// TODO: TODO 標(biāo)記
// FIXME: FIXME 標(biāo)記
println("Hello, world")
// 變量 (var) 的值設(shè)置后可以隨意改變
// 常量 (let) 的值設(shè)置后不能改變
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) // 類型轉(zhuǎn)換
let piText = "Pi = \(π), Pi 2 = \(π * 2)" // 格式化字符串
// 條件編譯
// 使用 -D 定義編譯開關(guān)
#if false
println("Not printed")
let buildValue = 3
#else
let buildValue = 7
#endif
println("Build value: \(buildValue)") // Build value: 7
/*
Optionals 是 Swift 的新特性,它允許你存儲兩種狀態(tài)的值給 Optional 變量:有效值或 None
Swift 要求所有的 Optinal 屬性都必須有明確的值,如果為空,則必須明確設(shè)定為 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 支持可保存任何數(shù)據(jù)類型的變量
// 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: 數(shù)組與字典(關(guān)聯(lián)數(shù)組)
//
/*
Array 和 Dictionary 是結(jié)構(gòu)體,不是類,他們作為函數(shù)參數(shù)時,是用值傳遞而不是指針傳遞。
可以用 `var` 和 `let` 來定義變量和常量。
*/
// Array
var shoppingList = ["catfish", "water", "lemons"]
shoppingList[1] = "bottle of water"
let emptyArray = [String]() // 使用 let 定義常量,此時 emptyArray 數(shù)組不能添加或刪除內(nèi)容
let emptyArray2 = Array<String>() // 與上一語句等價,上一語句更常用
var emptyMutableArray = [String]() // 使用 var 定義變量,可以向 emptyMutableArray 添加數(shù)組元素
// 字典
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: 控制流
//
// 數(shù)組的 for 循環(huán)
let myArray = [1, 1, 2, 3, 5]
for value in myArray {
if value == 1 {
println("One!")
} else {
println("Not one!")
}
}
// 字典的 for 循環(huán)
var dict = ["one": 1, "two": 2]
for (key, value) in dict {
println("\(key): \(value)")
}
// 區(qū)間的 loop 循環(huán):其中 `...` 表示閉環(huán)區(qū)間,即[-1, 3];`..<` 表示半開閉區(qū)間,即[-1,3)
for i in -1...shoppingList.count {
println(i)
}
shoppingList[1...2] = ["steak", "peacons"]
// 可以使用 `..<` 來去掉最后一個元素
// while 循環(huán)
var i = 1
while i < 1000 {
i *= 2
}
// do-while 循環(huán)
do {
println("hello")
} while 1 == 2
// Switch 語句
// Swift 里的 Switch 語句功能異常強大,結(jié)合枚舉類型,可以實現(xiàn)非常簡潔的代碼,可以把 switch 語句想象成 `if` 的語法糖
// 它支持字符串,類實例或原生數(shù)據(jù)類型 (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: 函數(shù)
//
// 函數(shù)是一個 first-class 類型,他們可以嵌套,可以作為函數(shù)參數(shù)傳遞
// 函數(shù)文檔可使用 reStructedText 格式直接寫在函數(shù)的頭部
/**
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")
// 函數(shù)參數(shù)前帶 `#` 表示外部參數(shù)名和內(nèi)部參數(shù)名使用同一個名稱。
// 第二個參數(shù)表示外部參數(shù)名使用 `externalParamName` ,內(nèi)部參數(shù)名使用 `localParamName`
func greet2(#requiredName: String, externalParamName localParamName: String) -> String {
return "Hello \(requiredName), the day is \(localParamName)"
}
greet2(requiredName:"John", externalParamName: "Sunday") // 調(diào)用時,使用命名參數(shù)來指定參數(shù)的值
// 函數(shù)可以通過元組 (tuple) 返回多個值
func getGasPrices() -> (Double, Double, Double) {
return (3.59, 3.69, 3.79)
}
let pricesTuple = getGasPrices()
let price = pricesTuple.2 // 3.79
// 通過下劃線 (_) 來忽略不關(guān)心的值
let (_, price1, _) = pricesTuple // price1 == 3.69
println(price1 == pricesTuple.1) // true
println("Gas price: \(price)")
// 可變參數(shù)
func setup(numbers: Int...) {
// 可變參數(shù)是個數(shù)組
let number = numbers[0]
let argCount = numbers.count
}
// 函數(shù)變量以及函數(shù)作為返回值返回
func makeIncrementer() -> (Int -> Int) {
func addOne(number: Int) -> Int {
return 1 + number
}
return addOne
}
var increment = makeIncrementer()
increment(7)
// 強制進(jìn)行指針傳遞 (引用傳遞),使用 `inout` 關(guān)鍵字修飾函數(shù)參數(shù)
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]
// 函數(shù)是閉包的一個特例
// 閉包實例
// `->` 分隔了閉包的參數(shù)和返回值
// `in` 分隔了閉包頭 (包括參數(shù)及返回值) 和閉包體
// 下面例子中,`map` 的參數(shù)是一個函數(shù)類型,它的功能是把數(shù)組里的元素作為參數(shù),逐個調(diào)用 `map` 參數(shù)傳遞進(jìn)來的函數(shù)。
numbers.map({
(number: Int) -> Int in
let result = 3 * number
return result
})
// 當(dāng)閉包的參數(shù)類型和返回值都是己知的情況下,且只有一個語句作為其返回值時,我們可以簡化閉包的寫法
numbers = numbers.map({ number in 3 * number })
// 我們也可以使用 $0, $1 來指代第 1 個,第 2 個參數(shù),上面的語句最終可簡寫為如下形式
// numbers = numbers.map({ $0 * 3 })
print(numbers) // [3, 6, 18]
// 簡潔的閉包
numbers = sorted(numbers) { $0 > $1 }
// 函數(shù)的最后一個參數(shù)可以放在括號之外,上面的語句是這個語句的簡寫形式
// numbers = sorted(numbers, { $0 > $1 })
print(numbers) // [18, 6, 3]
// 超級簡潔的閉包,因為 `<` 是個操作符函數(shù)
numbers = sorted(numbers, < )
print(numbers) // [3, 6, 18]
//
// MARK: 結(jié)構(gòu)體
//
// 結(jié)構(gòu)體和類非常類似,可以有屬性和方法
struct NamesTable {
let names = [String]()
// 自定義下標(biāo)運算符
subscript(index: Int) -> String {
return names[index]
}
}
// 結(jié)構(gòu)體有一個自動生成的隱含的命名構(gòu)造函數(shù)
let namesTable = NamesTable(names: ["Me", "Them"])
let name = namesTable[1]
println("Name is \(name)") // Name is Them
//
// MARK: 類
//
// 類和結(jié)構(gòu)體的有三個訪問控制級別,他們分別是 internal (默認(rèn)), public, private
// internal: 模塊內(nèi)部可以訪問
// public: 其他模塊可以訪問
// private: 只有定義這個類或結(jié)構(gòu)體的源文件才能訪問
public class Shape {
public func getArea() -> Int {
return 0;
}
}
// 類的所有方法和屬性都是 public 的
// 如果你只是需要把數(shù)據(jù)保存在一個結(jié)構(gòu)化的實例里面,應(yīng)該用結(jié)構(gòu)體
internal class Rect: Shape {
// 值屬性 (Stored properties)
var sideLength: Int = 1
// 計算屬性 (Computed properties)
private var perimeter: Int {
get {
return 4 * sideLength
}
set {
// `newValue` 是個隱含的變量,它表示將要設(shè)置進(jìn)來的新值
sideLength = newValue / 4
}
}
// 延時加載的屬性,只有這個屬性第一次被引用時才進(jìn)行初始化,而不是定義時就初始化
// subShape 值為 nil ,直到 subShape 第一次被引用時才初始化為一個 Rect 實例
lazy var subShape = Rect(sideLength: 4)
// 監(jiān)控屬性值的變化。
// 當(dāng)我們需要在屬性值改變時做一些事情,可以使用 `willSet` 和 `didSet` 來設(shè)置監(jiān)控函數(shù)
// `willSet`: 值改變之前被調(diào)用
// `didSet`: 值改變之后被調(diào)用
var identifier: String = "defaultID" {
// `willSet` 的參數(shù)是即將設(shè)置的新值,參數(shù)名可以指定,如果沒有指定,就是 `newValue`
willSet(someIdentifier) {
println(someIdentifier)
}
// `didSet` 的參數(shù)是已經(jīng)被覆蓋掉的舊的值,參數(shù)名也可以指定,如果沒有指定,就是 `oldValue`
didSet {
println(oldValue)
}
}
// 命名構(gòu)造函數(shù) (designated inits),它必須初始化所有的成員變量,
// 然后調(diào)用父類的命名構(gòu)造函數(shù)繼續(xù)初始化父類的所有變量。
init(sideLength: Int) {
self.sideLength = sideLength
// 必須顯式地在構(gòu)造函數(shù)最后調(diào)用父類的構(gòu)造函數(shù) super.init
super.init()
}
func shrink() {
if sideLength > 0 {
--sideLength
}
}
// 函數(shù)重載使用 override 關(guān)鍵字
override func getArea() -> Int {
return sideLength * sideLength
}
}
// 類 `Square` 從 `Rect` 繼承
class Square: Rect {
// 便捷構(gòu)造函數(shù) (convenience inits) 是調(diào)用自己的命名構(gòu)造函數(shù) (designated inits) 的構(gòu)造函數(shù)
// Square 自動繼承了父類的命名構(gòu)造函數(shù)
convenience init() {
self.init(sideLength: 5)
}
// 關(guān)于構(gòu)造函數(shù)的繼承,有以下幾個規(guī)則:
// 1\. 如果你沒有實現(xiàn)任何命名構(gòu)造函數(shù),那么你就繼承了父類的所有命名構(gòu)造函數(shù)
// 2\. 如果你重載了父類的所有命名構(gòu)造函數(shù),那么你就自動繼承了所有的父類快捷構(gòu)造函數(shù)
// 3\. 如果你沒有實現(xiàn)任何構(gòu)造函數(shù),那么你繼承了父類的所有構(gòu)造函數(shù),包括命名構(gòu)造函數(shù)和便捷構(gòu)造函數(shù)
}
var mySquare = Square()
println(mySquare.getArea()) // 25
mySquare.shrink()
println(mySquare.sideLength) // 4
// 類型轉(zhuǎn)換
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 構(gòu)造函數(shù),可能會返回 nil
init?(radius: Int) {
self.radius = radius
super.init()
if radius <= 0 {
return nil
}
}
}
// 根據(jù) 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 "?"
}
}
}
// 當(dāng)變量類型明確指定為某個枚舉類型時,賦值時可以省略枚舉類型
var suitValue: Suit = .Hearts
// 非整型的枚舉類型需要在定義時賦值
enum BookName: String {
case John = "John"
case Luke = "Luke"
}
println("Name: \(BookName.John.rawValue)")
// 與特定數(shù)據(jù)類型關(guān)聯(lián)的枚舉
enum Furniture {
// 和 Int 型數(shù)據(jù)關(guān)聯(lián)的枚舉記錄
case Desk(height: Int)
// 和 String, Int 關(guān)聯(lián)的枚舉記錄
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: 協(xié)議
// 與 Java 的 interface 類似
//
// 協(xié)議可以讓遵循同一協(xié)議的類型實例擁有相同的屬性,方法,類方法,操作符或下標(biāo)運算符等
// 下面代碼定義一個協(xié)議,這個協(xié)議包含一個名為 enabled 的計算屬性且包含 buildShape 方法
protocol ShapeGenerator {
var enabled: Bool { get set }
func buildShape() -> Shape
}
// 協(xié)議聲明時可以添加 @objc 前綴,添加 @objc 前綴后,
// 可以使用 is, as, as? 等來檢查協(xié)議兼容性
// 需要注意,添加 @objc 前綴后,協(xié)議就只能被類來實現(xiàn),
// 結(jié)構(gòu)體和枚舉不能實現(xiàn)加了 @objc 的前綴
// 只有添加了 @objc 前綴的協(xié)議才能聲明 optional 方法
// 一個類實現(xiàn)一個帶 optional 方法的協(xié)議時,可以實現(xiàn)或不實現(xiàn)這個方法
// optional 方法可以使用 optional 規(guī)則來調(diào)用
@objc protocol TransformShape {
optional func reshaped()
optional func canReshape() -> Bool
}
class MyShape: Rect {
var delegate: TransformShape?
func grow() {
sideLength += 2
// 在 optional 屬性,方法或下標(biāo)運算符后面加一個問號,可以優(yōu)雅地忽略 nil 值,返回 nil。
// 這樣就不會引起運行時錯誤 (runtime error)
if let allow = self.delegate?.canReshape?() {
// 注意語句中的問號
self.delegate?.reshaped?()
}
}
}
//
// MARK: 其它
//
// 擴(kuò)展: 給一個已經(jīng)存在的數(shù)據(jù)類型添加功能
// 給 Square 類添加 `Printable` 協(xié)議的實現(xiàn),現(xiàn)在其支持 `Printable` 協(xié)議
extension Square: Printable {
var description: String {
return "Area: \(self.getArea()) - ID: \(self.identifier)"
}
}
println("Square: \(mySquare)") // Area: 16 - ID: defaultID
// 也可以給系統(tǒng)內(nèi)置類型添加功能支持
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` 關(guān)鍵字來限制類型。
// 如果只有一個類型限制,可以省略 `where` 關(guān)鍵字
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 的數(shù)學(xué)運算符等
prefix operator !!! {}
// 定義一個前綴運算符,使矩形的邊長放大三倍
prefix func !!! (inout shape: Square) -> Square {
shape.sideLength *= 3
return shape
}
// 當(dāng)前值
println(mySquare.sideLength) // 4
// 使用自定義的 !!! 運算符來把矩形邊長放大三倍
!!!mySquare
println(mySquare.sideLength) // 12
更多建議: