Swift 语言最小化

swift_tour_learning-swift.jpg

WWDC 2014 上 Apple Duang!!! 一声发布了 Swift。在那次发布会上, Apple 向外界宣布了,自己其实是一家软件公司。

2015 年, Apple 又 Duang!!! 一声将 Swift 开源。这让 Swift 迅速攻占了服务器,这使 Swift 开发 Backend server 成为了可能( Vapor )。

初次接触 Swift,让我看到了 Scala、C#、ES 6 的影子,并且语法层面兼容 Objective C。first-class functions 特性让语言并不局限于 OO,让 Swift 具有函数式编程语言的表达力。 同时,Swift 作者 Chris Lattner 这位传奇人物的存在,让 Swift 成为值得长期投资的一门语言。

本文来自 Swift Tour,将其中的内容归类总结,可以当做 Cheatsheet 翻阅,对于具有经验的开发者,看过这篇便可开始工作了。

变量

Swift 中可以使用 varlet 直接定义 变量常量

定义变量:

var age = 20// 定义变量
let name = "Halo" // 定义常亮
age = 21

声明类型:

上段代码看起来有些类似 javascript 若类型语言的特性,Swift 本质上还是强类型的语言,声明变量的时候,可以指定数据类型:

let cash: Float = 1.25
let money: Double = 1.252525
let dollar: String = "USD"

使用 String

对于字符串,Swift 简化了 Objective C NSString 的定义方式:@""

let currencies = "AUD, " + dollar
let rmb = "1 \(dollar) is 6 RMB"
let formattedString = String(format: "%.2f \(dollar) is about 600 RMB", 100.0) // 100.00 USD is about 600 RMB

\(variable) 是 Swift 为字符串模板提供的支持。<s>不过 Swift 还是支持类似 ES 6 或者 Ruby 中多行字符串模板。多行字符串还是需要 + 号拼接</s> Swift 4.2 开始支持多行字符串:

let introduceSwift = """
  We are excited by this new chapter in the story of Swift.
  After Apple unveiled the Swift programming language, it quickly became one of
  the fastest growing languages in history. Swift makes it easy to write software
  that is incredibly fast and safe by design. Now that Swift is open source, you can
  help make the best general purpose programming language available everywhere.
"""

使用 tuple

tuple 是 Swift 引入的一种新的数据类型,它可以方便支持方法的多返回值:

let numbersTuple = (1, 100, 1000)
    print("min: \(numbersTuple.0), max: \(numbersTuple.1), sum: \(numbersTuple.2)")

let numbersNamedTuple = (min: 1, max: 100, sum: 1000)
    print("min: \(numbersNamedTuple.0), max: \(numbersNamedTuple.1), sum: \(numbersNamedTuple.2)")
    print("min: \(numbersNamedTuple.min), max: \(numbersNamedTuple.max), sum: \(numbersNamedTuple.sum)")

Collections

创建集合:

Swift 的集合类型主要还是 ArrayDictionary

var fruits = ["apple", "banana", "oranage"]
var fruitsPrice = [
    "apple": 4.25,
    "banana": 6.2
]
print(fruitsPrice[fruits[1]]) // 6.2

定义空集合:

var emptyArray = [String]()
var emptyDictionary = [String: Float]()

将已有集合设置为空时,可以用如下简写:

fruits = []
fruitsPrice = [:]

使用 for in 遍历集合:

for score in [1,2,3,4,5,6] {
    print(score * score * score)
}

for (key, val) in ["one": 1, "two": 2] {
    print("\(val) is \(key)")
}

if else 和 Optional

Swift 中新增一种类型 Optional,这个概念来自 Scala。 Optional 类似一个包装器,它可以包装一个数值,但是 Optional 可能返回 nil,可能返回数据。多用于错误处理,通常跟 if else 连用:

声明 Optional:

var optionalString: String? = nil
//print(optionalString!) // nil 取值会报错
print(optionalString == nil)

var optionalName: String? = "Jian"
print(optionalName!)

String? 表示一个 String 类型的 Optional。只有 Optional 可以接受 nil 值。如果对一个 nil 的 Optional 取值会报错,一般取值前会对 Optional 做判断:

var optionalString: String? = nil

if optionalString != nil {
    let val = optionalString!
} else {
    print("There is no value")
}

if-else 简化 Optional 取值:

Swift 为上面这种方式提供一种简写:

if let val = optionalString {
    print("There is a value: \(val)")
} else {
    print("There is no value")
}

// flatMap
optionalString.flatMap { print("There is a value: \($0)") }

通常会将 if-else 和 Optional 这么使用:

var optionalName: String? = "Jian"
print(optionalName!)

var greeting = "Hello!"

if let name = optionalName {
    greeting = "Hello \(name)"
}

Optional 默认值:

使用 ?? 可以为 Optional 提供默认值:

let fullName: String = "Jian"
let nickName: String? = nil
let displayName = "\(nickName ?? fullName)"

switch

Swift 中的 switch 强大很多,支持 String,多值匹配,条件匹配:

let vegetable = "red pepper"

switch vegetable {
case "celery":
    print("Add some raisins and make ants on a log.")
case "cucumber", "watercress":
    print("Taht would make a good tea sandwich.")
case let x where x.hasSuffix("pepper"):
    print("Is it a spicy \(x)?") // Is it a spicy red pepper?
default:
    print("Everythink tasts good")
}

使用循环

for in 循环

Swift 中的 for in 和 javascript 中的有点类似。使用 for in 便利 Dictionary:

let numbersDict = [
    "Prime": [2, 3, 5, 7, 11, 13],
    "Fibonacci": [1, 1, 2, 3, 5, 8],
    "Square": [1, 4, 9, 16, 25]
]

var largest = 0

for (kind, numbers) in numbersDict {
    for number in numbers {
        number
        if number > largest {
            largest = number
        }
    }
}
print(largest) // 25

使用 for in 遍历 Range:

var total = 0
for i in 0..<4 {
    total += i
}
print(total) // 6

while 循环

一般语言都会提供 whiledo {} while 循环。Swift 采用 whilerepeat {} while

var n = 1
while n < 100 {
    n += n
}
print(n) //128

var m = 1
repeat {
    m += m
} while m < 100
print(m) // 128

Functions

Swift 中的函数具有 javascript 和 objective c 的双重特征。并且函数在 Swift 中是一等公民(First-class type),具有高阶函数的特性。

定义函数

一般情况下,跟 Objective C 方法一样,需要给函数参数提供 Label:

func greet(person: String, day: String) -> String {
    return "Hello \(person), today is \(day)."
}
greet(person: "Jian", day: "Friday")

使用 _ 表示省略 Label:

func greet(_ person: String, on day: String) -> String {
    return "Hello \(person), today is \(day)."
}
greet("Jian", on: "Friday")

函数可以使用 tuple 返回多个值:

func calcStatistics(scores: [Int]) -> (min: Int, max: Int, sum: Int) {
    var min = scores[0]
    var max = scores[0]
    var sum = 0
    
    for score in scores {
        if score > max {
            max = score
        } else if score < min {
            min = score
        }
        
        sum += score
    }
    
    return (min, max, sum)
}

let statistics = calcStatistics(scores: [5, 1, 100, 30, 90])
print(statistics.sum) // 226
print(statistics.2)   // 226

无限参数:

func sumOf(numbers: Int...) -> Int {
    return numbers.reduce(0, +)
}
sumOf()                             // 0
sumOf(numbers: 1, 2, 3, 4, 10)      // 20

使用 Int... 在函数的最后一个参数,表示后面可以接受无限个整数参数。

函数嵌套

func returnFifteen() -> Int {
    var y = 10
    func add() { y += 5 }
    add()
    return y
}
returnFifteen() // 15

高阶函数

高阶函数(high-order function),是函数是编程的一个概念,高阶函数的特点:

  • 可以将函数当做返回值,即:可以使用函数创建函数
  • 可以将函数当做参数接收

使用函数创建另一个函数:

func createIncrementer() -> ((Int) -> Int) {
    func plusOne(number: Int) -> Int {
        return 1 + number
    }
    
    return plusOne
}
let incrementer = createIncrementer()
incrementer(10)

函数作为参数:

func hasAnyMatches(_ list: [Int], matcher: (Int) -> Bool) -> Bool {
    for item in list {
        if matcher(item) {
            return true
        }
    }
    
    return false
}

func lessThanThen(number: Int) -> Bool {
    return number < 10
}

hasAnyMatches([20, 10, 7, 12], matcher: lessThanThen)

Closure (闭包)

Closure 可以理解匿名函数,在很多 Callback 和集合操作中使用:

hasAnyMatches([20, 10, 7, 12]) { (item: Int) -> Bool in
    item < 10
}
[20, 10, 7, 12].map { (item: Int) -> Int in
    return 3 * item
}
[20, 10, 7, 12].map({ item in 3 * item })

如果省略 () 后, Closure 是唯一的参数:

let sortedNumbers = [20, 10, 7, 12].sorted { $0 > $1 }
print(sortedNumbers)

Objects and Classes

定义和使用类:

class Shape {
    var numberOfSides = 0
    
    func simpleDescription() -> String {
        return "A shap with \(numberOfSides) sides."
    }
}
let shape = Shape()
shape.numberOfSides = 10
shape.simpleDescription()

使用继承:

class NamedShape : Shape {
    var name: String
    
    init(name: String) {
        self.name = name
    }
}

// inherit and override
class Square: NamedShape {
    var sideLength: Double
    
    init(sideLength: Double, name: String) {
        self.sideLength = sideLength
        super.init(name: name)
        numberOfSides = 4
    }
    
    func area() -> Double {
        return sideLength * sideLength
    }
    
    override func simpleDescription() -> String {
        return "A square with sides of length \(sideLength)"
    }
}

let square = Square(sideLength: 10.0, name: "Jian's Square")
square.area()
square.simpleDescription()

使用属性

Swift 中对属性的处理,有些像 C#。

使用 getter/setter

class EquilateralTriangle: NamedShape {
    var sideLength: Double = 0.0
    
    init(sideLength: Double, name: String) {
        self.sideLength = sideLength
        super.init(name: name)
        numberOfSides = 3
    }
    
    var perimeter: Double {
        get {
            return 3.0 * sideLength
        }
        set {
            sideLength = newValue / 3.0
        }
    }
    
    override func simpleDescription() -> String {
        return "An equilateral triangle with sides of length \(sideLength)"
    }
}
var triangle = EquilateralTriangle(sideLength: 3.1, name: "A triangle")
print(triangle.perimeter)           // 9.3

triangle.perimeter = 9.9
print(triangle.sideLength)          // 3.3

使用 willSet:

class TriangleAndSquare {
    var triangle: EquilateralTriangle {
        willSet {
            square.sideLength = newValue.sideLength
        }
    }
    
    var square: Square {
        willSet {
            triangle.sideLength = newValue.sideLength
        }
    }
    
    init(size: Double, name: String) {
        square = Square(sideLength: size, name: name)
        triangle = EquilateralTriangle(sideLength: size, name: name)
    }
}

var triangleAndSquare = TriangleAndSquare(size: 10, name: "other shape")
print(triangleAndSquare.square.sideLength)          // 10.0
print(triangleAndSquare.triangle.sideLength)        // 10.0

triangleAndSquare.square = Square(sideLength: 50, name: "Large square")
print(triangleAndSquare.triangle.sideLength)        // 50.0

使用 willSet 保证 squaretrianglesideLength 始终相等。

使用 Optional 类型

使用 ?. 取值,当值为 nil 时返回 nil,而不是报错。

let optionalSquare: Square? = Square(sideLength: 2.5, name: "optional square")
let sideLength = optionalSquare?.sideLength
let nilSquare: Square? = nil
let nilLength = nilSquare?.sideLength

Enumerations

定义 enum 类型:

enum Level: Int {
    case Zero, First, Second
}
Level.Zero.rawValue
Level.First.rawValue

enumrawValue 默认从 0 开始,依次递增。

在 enum 中创建方法:

enum Suit {
    case spades, hearts, diamonds, clubs
    func simpleDescription() -> String {
        switch self {
        case .spades:
            return "spades"
        case .hearts:
            return "hearts"
        case .diamonds:
            return "diamonds"
        case .clubs:
            return "clubs"
        }
    }
}

定义特性类型的 enum:

enum Rank: Int {
    case ace = 1
    case two, three, four, five, six, seven, eight, nine, ten
    case jack, queen, king
    
    func simpleDescription() -> String {
        switch self {
        case .ace:
            return "ace"
        case .jack:
            return "jack"
        case .queen:
            return "queen"
        case .king:
            return "king"
        default:
            return String(self.rawValue)
        }
    }
}
let ace = Rank.ace                      // ace
let aceRawValue = ace.rawValue          // 1

enum Rank: Int 设置 enumrawValueInt。我们也可以通过 rawValue 来创建 enum

if let convertedRank = Rank(rawValue: 3) {
    let threeDescription = convertedRank.simpleDescription()        // 3
}

复杂的 enum

enum ServerResponse {
    case result(String, String)
    case failure(String)
}

let success = ServerResponse.result("6:00 am", "8:00 pm")
let failure = ServerResponse.failure("Out of cheese")

switch success {
case let .result(sunrise, sunset):
    print("Sunrise is at \(sunrise) and sunset is at \(sunset)") // Sunrise is at 6:00 am and sunset is at 8:00 pm
case let .failure(message):
    print("Failure ... \(message)")
}

Structs

struct Card {
    var rank: Rank
    var suit: Suit
    
    func simpleDescript() -> String {
        return "The \(rank.simpleDescription()) of \(suit.simpleDescription())"
    }
}
let threeOfSpades = Card(rank: .three, suit: .spades)   // Card
print(threeOfSpades.simpleDescript())                   // The 3 of spades

structclass 最重要的区别是:

  • struct 传递值,始终传递 copy
  • class 传递引用。

Protocol

protocol ExampleProtocol {
    var simpleDescription: String { get }
    mutating func adjust()
}

Protocol 与 Java 中的 Interface 概念类似。在 Swift 中,class、struct、enum、extension 都可以实现 Protocol:

class 实现 protocol:

class SimpleClass: ExampleProtocol {
    var simpleDescription: String = "A simple class."
    var moreProperty: Int = 315
    func adjust() {
        simpleDescription += " Now 100% adjusted"
    }
}
var simpleClass = SimpleClass()
simpleClass.adjust()
print(simpleClass.simpleDescription)        // A simple class. Now 100% adjusted

struct 实现 protocol:

struct SimpleStructure: ExampleProtocol {
    var simpleDescription: String = "A simple structure"
    mutating func adjust() {
        simpleDescription += " (adjusted) "
    }
}

var simpleStruct = SimpleStructure()
simpleStruct.adjust()
print(simpleStruct.simpleDescription)

mutating 修饰符表示,当前方法在 struct 中可以修改 struct 的值。

在 protocol 的应用中,可以使用 Java 中面向 Interface 编程:

let protocolValue: ExampleProtocol = simpleStruct
print(protocolValue.simpleDescription)              // A simple structure (adjusted)

Extensions

Swift 中的 extension 和 ruby 中的 Open Class 概念很像,在 ObjC 中是 Category。它可以为已有类型添加新的特性:

extension Int: ExampleProtocol {
    var simpleDescription: String {
        return "The number \(self)"
    }
    
    mutating func adjust() {
        self += 10
    }
}

print(7.simpleDescription)

Error Handling

throws 异常

enum PointerError: Error {
    case outOfPaper
    case noToner
    case onFire
}

// throw error
func send(job: Int, toPointer printerName: String) throws -> String {
    if printerName == "Never has toner" {
        throw PointerError.noToner
    }
    return "Job sent"
}

catch 和处理异常

对于具有 throws 声明的方法,需要使用 try 关键字调用,然后使用 do {} catch {} 包裹:

do {
    let pointerResp = try send(job: 1024, toPointer: "Never has toner")
    print(pointerResp)
} catch { // catch all errors
    print(error) // use error by default
}

处理多个异常:

do {
    let pointerResponse = try send(job: 1024, toPointer: "Jian")
    print(pointerResponse)
    throw PointerError.onFire
    
} catch PointerError.onFire {
    print("I'll just put shi over here, with the result of the fire.")      // I'll just put shi over here, with the result of the fire.
} catch let pointerError as PointerError {
    print("Printer error: \(pointerError) .")
} catch {
    print(error)
}

通常 catch 多个 Error 时,catch 顺序需要从小异常,到范围更大的异常。

使用 try?

// try? -> Optional
let pointerFailure = try? send(job: 1024, toPointer: "Never has toner")         // nil
let pointerSuccess = try? send(job: 2048, toPointer: "Good pointer")        // Job sent

使用 defer

在方法 throws 时,会终端当前函数后续的代码执行,使用 defer 可以确保 defer 代码段中的代码在函数返回前始终被执行。这个概念有些像 Java 的 try {} catch {} finally {} 中的 finally

func ensureExecute() throws -> String {
    defer {
        print("Complete")
    }
    
    do {
        try send(job: 1024, toPointer: "Never has toner")
    } catch {
        throw error
    }
    
    return "Executed"
}
let executed = try? ensureExecute()     // Complete

Generic

Swift 开始支持泛型(Generic):

enum OptionalValue<Wrapped> {
    case none
    case some(Wrapped)
}
var possibleInteger: OptionalValue<Int> = .none     // none
possibleInteger = .some(100)                        // some(100)

使用 where 限定泛型的类型:

func anyCommonElements<T: Sequence, U: Sequence>(_ lhs: T, _ rhs: U) -> Bool
    where T.Iterator.Element: Equatable, T.Iterator.Element == U.Iterator.Element {
        
        for lhsItem in lhs {
            for rhsItem in rhs {
                if lhsItem == rhsItem {
                    return true
                }
            }
        }

        return false
}
anyCommonElements([1,2,3,4,5], [5])

where T.Iterator.Element: Equatable, T.Iterator.Element == U.Iterator.Element 限制 TU 中的元素,必须实现 Equatable,并且 TU 中的元素是同一种类型。

扩展阅读

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 157,012评论 4 359
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,589评论 1 290
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 106,819评论 0 237
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,652评论 0 202
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 51,954评论 3 285
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,381评论 1 210
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,687评论 2 310
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,404评论 0 194
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,082评论 1 238
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,355评论 2 241
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,880评论 1 255
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,249评论 2 250
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,864评论 3 232
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,007评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,760评论 0 192
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,394评论 2 269
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,281评论 2 259

推荐阅读更多精彩内容