5

Apple在WWDC 2014开发者大会上发布了用于Mac OS X和iOS编程的新一代编程语言Swift。

在真正能够使用Swift之前,SF先带大家从Apple的Beta版文档中,简单的一览Swift的基础用法。

请输入图片描述

SEGMENTFAULT声明:本文是根据**未发布版本**的文档编译整理而成的。
我们无法保证苹果公司未来实际发布的软件,与本文的情况完全一致。

注:不要错过文章末尾的《SF简评Swift》部分!

101:最基本语法

  • 编译型(LLVM)
  • 等同众多解释型语言的实时输入、实时执行(Playground)
  • 全局域自动执行(无需主函数)
  • 行尾无分号
  • Hello world:println("Hello world")
  • 注释使用///* */。但块注释可以嵌套
    例如:/* A /* B */ C */就是合法的。
  • =运算符仅用于赋值
    与C不同:var = value不是一个合法的表达式
    与C一致的是==运算符仍然是判断

变量与数据类型

  • 弱类型
  • 变量:var length = 10
  • 常量:let MAX_LENGTH = 100
  • 强制类型:var length: Double = 10 (var or let)
  • 完全禁止隐式转换,例如这个是错误的:
    println("Length = " + length + " cm")
    必须这样写:
    println("Length = " + String(length) + " cm")
    但是在字符串中嵌入数字有一种简写法:
    println("Length = \(length) cm")

空值

  • 空值表示为nil
  • 必须使用?,显式指定变量可以赋空值:var optionalString: String? = nil (定义或运行时均可)
  • nil在逻辑判断中被隐式转换为false (这与通常的变量值不得隐式转换不同)

另外,如果待引用的变量值可能是nil,一般需要先行判断。但在Swift中,可以在下标、方法或对象成员的引用之前加?
这个语法表示如果?前边的是nil,则后边的调用全部省略,整个表达式的值是nil

WWDC 2014会上,Craig Federighi 直接展示了这个用法,将数行带有预判断的代码缩成了一行:
包含数个if变量不等于nil的多行语句
<code>myDelegate?.scrollViewDidScroll?(myScrollView)</code>

数组与字典

  • 全部使用方括号包裹
  • 数组:[value 1, value 2, value 3]
  • 数组下标从0开始
  • 字典:["key1": value 1, "key2": value 2]
  • 空数组:[],空字典:[:]

流程控制

注意:Swift中任何条件判断,必须使用真正的布尔值表达式!数值不会按照“0/非0”的习惯规则隐式转换为true,而是一律报错

条件分支

if 判断

if condition 1 {
    code for condition 1
} else if condition 2 {
    code for condition 2
} else {
    code for none of above
}
  • if语句中,小括号可选,大括号必须

switch 多出口分支

switch variable {
    case value 1:
        code for value 1
    case value 2:
        code for value 1
    default:
        code for none of abolve
}
  • 无需break
  • 可以一次比较多个值:case value 1, value 2: (值1或值2)
  • 比较条件不限于值,可以使用自定义的语句:case let x where x > 10:
  • default分支不得省略。

循环

for-in 遍历循环

for var_iterator in iterated_array {
    code within loop
}
  • 循环字典:for (key, value) in iterated_dictionary
  • 使用..指定整数的范围:for index in 1..5

for-condition-increment C风格循环

for var index = 0; index < 3; ++index {
    code within loop
}

while 循环

while condition {
    code
}

do {
    code
} while condition

函数

定义与调用

func greet(name: String, day: String) -> String {
    return "Hello \(name), today is \(day)."
}
greet("Bob", "Tuesday")
  • 一次传入多个值:定义func f1(p1: Int...) -> Int {code} 调用f1(1, 2, 3)
  • 一次返回多个值:return (3.59, 3.69, 3.79) (借助tuple)

函数允许嵌套。子函数共享父函数的变量作用域:

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

闭包特性

函数本身是一种数据类型。函数可以作为返回值:

func makeIncrementer() -> (Int -> Int) {
    func addOne(number: Int) -> Int {
        return 1 + number
    }
    return addOne
}
var increment = makeIncrementer()
increment(7)

也可以把函数本身,作为参数传递给其他函数:

func hasAnyMatches(list: Int[], condition: Int -> Bool) -> Bool {
    for item in list {
        if condition(item) {
            return true
        }
    }
    return false
}
func lessThanTen(number: Int) -> Bool {
    return number < 10
}
var numbers = [20, 19, 7, 12]
hasAnyMatches(numbers, lessThanTen)

但必须注意类型必须严格等同。

可以使用{}定义匿名函数。可以在花括号内的第一行,使用in关键字定义参数和返回值:

numbers.map({
    (number: Int) -> Int in
    let result = 3 * number
    return result
})
  • 但也可以在类型已知的情况下省略之。例如:numbers.map({ number in 3 * number })
    这个单行的闭包,只接受一个参数并返回表达式的值。
  • 也可以使用数字引用参数。例如:sort([1, 5, 3, 12, 2]) { $0 > $1 }
    这个传参方式类似Shell。

对象与类

定义与实例化

定义:

class Shape {
    var numberOfSides = 0
    var name: String
    init(name: String) {
        self.name = name
    }
    func simpleDescription() -> String {
        return "A shape with \(numberOfSides) sides."
    }
}

实例化:

var shape = Shape("Triangle")
shape.numberOfSides = 3
println(shape.simpleDescription())
  • 构造:init(name: String) {self.name = name}
  • 析构:deinit() {code}

继承与多态

  • 继承在类名后边加:
  • 使用super关键字引用父类
  • 多态必须显式使用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 test = Square(sideLength: 5.2, name: "my test square")
test.area()
test.simpleDescription()

属性(property)

能够自定义赋值或取值的代码的变量被称为属性(property)。

  • 使用get自定义取值,使用set自定义赋值
  • get代码段中使用return返回取值结果
  • set代码段中固定使用newValue来表示传入的值
  • 也可以使用willSetdidSet,在赋值前后执行代码(不影响赋值本身)
class Circle {
    init(radius: Double) {
        self.radius = radius
    }
    var radius: Double {
        didSet {
            perimeter = radius * 6.28
        }
    }
    var perimeter: Double {
        get {
            return 6.28 * radius
        }
        set {
            radius = newValue / 6.28
        }
    }
}
circle = Circle(10)
circle.perimeter // 62.8
circle.perimeter = 31.4
circle.radius // 5

枚举与结构体

使用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.toRaw())
        }
    }
}
let ace = Rank.Ace
let aceRawValue = ace.toRaw() // 1

使用toRawfromRaw转换原始值和字面值:

if let convertedRank = Rank.fromRaw(11) {
    let jackDescription = convertedRank.simpleDescription() // "Jack"
}
let jackIndex = Rank.toRaw(Rank.Jack) // 11

上边的例子中,由于扑克牌存在数字意义的原始值,所以从Ace开始提供了一个1,而后按顺序自动增加。但Swift的枚举类型,为了没有数字意义的枚举量,也支持不使用原始值的定义方法:enum Color {case Red, Green, Blue}

也可以在枚举量上开辟变量空间,用来存储一些必要的值:

enum ServerResponse {
    case Result(String, String)
    case Error(String)
}

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

switch success {
    case let .Result(sunrise, sunset):
        let serverResponse = "Sunrise is at \(sunrise) and sunset is at \(sunset)."
    case let .Error(error):
        let serverResponse = "Failure... \(error)"
}

结构体

结构体使用struct定义,其定义语法和类非常相似。结构体支持构造器、成员方法等类的特性。

注意:结构体是按值传递的,而类/对象是按引用传递的。

struct Card {
    var rank: Rank
    var suit: Suit
    func simpleDescription() -> String {
        return "The \(rank.simpleDescription()) of \(suit.simpleDescription())"
    }
}
let threeOfSpades = Card(rank: .Three, suit: .Spades)
let threeOfSpadesDescription = threeOfSpades.simpleDescription()

协议和扩展

使用protocol定义协议:

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

协议可以用在类、枚举和结构体上。

class SimpleClass: ExampleProtocol {
    var simpleDescription: String = "A very simple class."
    var anotherProperty: Int = 69105
    func adjust() {
        simpleDescription += " Now 100% adjusted."
    }
}
var a = SimpleClass()
a.adjust()
let aDescription = a.simpleDescription

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

extension定义扩展,用来为已有的类型增加新的功能:

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

泛型

使用<>声明泛型:

func repeat<ItemType>(item: ItemType, times: Int) -> ItemType[] {
    var result = ItemType[]()
    for i in 0..times {
        result += item
    }
    return result
}
repeat("knock", 4)

泛型可以用在类、枚举和结构体上。

// Reimplement the Swift standard library's optional type
enum OptionalValue<T> {
    case None
    case Some(T)
}
var possibleInteger: OptionalValue<Int> = .None
possibleInteger = .Some(100)

使用where对泛型提出一些约束:

func anyCommonElements <T, U where T: Sequence, U: Sequence, T.GeneratorType.Element: Equatable, T.GeneratorType.Element == U.GeneratorType.Element> (lhs: T, rhs: U) -> Bool {
    for lhsItem in lhs {
        for rhsItem in rhs {
            if lhsItem == rhsItem {
                return true
            }
        }
    }
    return false
}
anyCommonElements([1, 2, 3], [3])

SF简评Swift

Swift无疑是本次WWDC 2014中,贴近苹果开发者的最大亮点。

Swift并不是对语言思想的本质革新,而是从当前的各种优秀语言中博采众家之长,从而制成为苹果平台提供的优秀工具。

Swift尤其是对于各种“坑”的处理非常用心。例如:

  • 嵌套注释
  • 封锁隐式类型转换
  • 封锁隐式逻辑判断
  • ……

这些对于新手和老手而言,都是好的。“减轻新手痛苦,节约老手时间”——我想起了这一句广告词。

我们相信Swift(至少在部分场合下)作为Objective-C的代用语言,将在苹果开发过程中发挥不可或缺的作用。

我们目前主要对Swift存疑的地方有:

  1. 浮夸的Benchmark数据。
    WWDC大会上Swift宣传相对Python等语言数十至数百倍的性能提升,是一种纯粹的广告口径——因为优秀编程语言之间出现如此巨大的性能差距,这本身就不太现实。我们期待将来能有第三方,对Swift本身给出资料和样例数据充分的公正的Benchmark。
  2. Swift的开源程度。
    编程语言作为系统基础工具,其行为的透明性是非常关键的。斯诺登把世界吓怕了——希望自己的代码能够放心使用,而不至于在编程语言这一层面,就存在被植入预期以外的行为,这是现在的某些开发者客观存在的一个合理担忧。
  3. Swift的应用领域。
    Swift究竟会成为一门通用的编程语言,还是只是作为苹果SDK的一部分而存在?这一点和Swift的开源程度是有重叠的——开源也许就意味着广为使用,而闭源就等同于Swift专用于苹果平台。

我们不愿意做任何的预先判断,只期待Swift公布后实际的表现如何。

Swift相关阅读推荐

语言介绍

苹果官方文档:(Pre-relase,发布前版本)
A Swift Tour, The Swift Programming Language, iOS Developer Library — Pre-Release
本文很多内容来自于对该文档的翻译。
来自其他作者的同一译文:来自苹果的编程语言——Swift简介

网络讨论

如何评价 Swift 语言?
新发布的 Swift 语言对初学者来说是新的机遇吗?


SegmentFault编译原创文章,转载请遵守本站相关声明。
外文原作者:Apple Inc.
编译与原创部分:沙渺
责任编辑:沙渺


沙渺
21.8k 声望1.1k 粉丝

1998年入行,普通的电脑老玩家。Web、嵌入式Linux和电子产品设计研究者。