Swift 语言入门

开发环境

官方推荐使用xcode进行编码,但是使用xcode的前提是必须拥有一台Mac电脑(或者黑苹果),因为xcode只能运行在苹果的系统上。

但是如果有ipad也可以用ipad上的Swift Playground这款App先做一些学习和尝试,上面也可以运行playground代码和构建app。

Swift的历史和特点

2014年由Apple公司推出,后来开源。来源于C和Objective-C语言,拥有很多其中的特征。

Swift具有如下一些特性:

  • 语法简洁。
  • 可选类型,意味着有些值可以不存在。类似于Python中的Optional[]。
  • 类型推断。只需要通过let和var来声明类型,不需要指定具体的类型,编译器会完成类型的推断工作。
  • 类型安全。
  • 自动引用计数ARC。
  • 元组和多个返回值。
  • 泛型。
  • 快速在集合上进行迭代。
  • 支持方法、扩展和协议的结构体。
  • map filter reduce等常用的函数式贬称规模性支持。
  • 强大的错误处理机制。

不需要分号结尾,不需要严格的缩进。注释用//和/**/表示。///表示文档注释,会出现在按 Option 键点击查看文档的时候。文档注释中如果使用- Parameter xxx: xxxx 表示解释传入的参数 - Note: 表示其他笔记说明

常量、变量数据类型

常量通过let声明,一旦声明无法更改。

变量通过var声明,声明之后仍然可以对其值进行修改。

尽管var可以替代let,但是声明成常量仍然有一些好处,比如避免修改常量的值让程序更安全,编译器会针对常量进行优化

变量命名规范:官方推荐采用小驼峰方式对变量进行命名,实际上变量中可以出现各种字符(除了空白字符和数学符号)

常见类型:

  • Int 整数
  • Double 双精度浮点数
  • Bool 布尔,true和false,小写。
  • String 字符串
  • Array 数组
  • Dictionary 字典
  • Set 集合
  • Sequence 序列
  • Error 错误

可以自定义类型,形式略像C的结构体:

struct Person{
    let firstName: String
    let lastName: String
    
    func sayHello() {
        print("hello, my name is \(firstName) \(lastName).")
    }
}

let person: Person = Person(firstName:"haha", lastName:"yes")
person.sayHello()

在Swift中,变量和常量在声明的时候类型虽然不需要明确指定,但是已经被编译器推断并确定了。之后,不能像python一样随意改变一个变量(常量)的类型,否则会引发错误,这就是Swift的类型安全。这种类型安全要求很严格,即使是Int和Double之间也必须遵循,不会存在C和C++中的那种隐式类型转换。

表示数字时,可以在数字中间加下换线来方便阅读,例如199_2等同于1992。

尽管编译器会帮忙在声明变量和常量时推断类型,也可以用冒号(:)类型标注,更加方便阅读。var playScore:Double = 19 语句中,会进行隐式类型转换,这说明,变量赋值之前的隐式转换还是存在的,编译器会先匹配手动声明的类型完成转换再赋值。

类型标注通常用在:只声明不初始化,不希望被推断(如Character类型可能被推断为String),声明自定义类型的成员变量。

关于类型,Swift的原则是,必须要标注或者能推断出变量和常量的类型,否则不通过。不能使用没有赋值的变量和常量。对于常量,如果声明的时候没有赋值,则后来只能赋值一次

另外还可以声明一些其他类型的数字,如UInt8表示0~255之间的8位整数。

运算符

Swift支持赋值(=) 四则运算(+-*/)

对于二元运算符,如果两边类型不同,通常是无法计算的。字面量的整数会被转换为Double运算,而变量不行。对于Int型相除,结果是向下取整的。

var haha = 1 * 1.2 // ok, 结果1.2
var h1 = 1, h2 = 1.2
var haha2 = h1 * h2  // 不行,报错,Int和Double不能相乘

常见的复合赋值运算符(+= -= *= /=)都可以使用。

可以用%来求余数,但是只支持整数取模。

类型转换:所有需要用到类型转换的地方都需要显式指明。例如let pi = Double(x) + y

控制流程

逻辑、比较运算符:== != > <= < && ||! 和C语言一样的。

if语句:

let temperature = 100
if temperature >= 100 {
    print("steam")
} else if temperature <= 0 {
    print("ice")
} else {
    print("water")
}

switch语句(同C):

switch numberOfWheels {
case 1:
    print("Unicycle")
case 2:
    print("Bicycle")
case 3:
    print("Tricycle")
case 4:
    print("Quadcycle")
case 5, 6, 7:
    print("Too many")
case ...0: // 包含0
    print("error")
case 8...12: // 包含8,12
    print("Too too many")
case 13...: // 包含13
    print("error")
default:
    print("error")
}

switch语句中必须有default。范围选择时包含两端。

三元运算符 ? : 也可以用。largest = a > b ? a : b

函数

函数声明和调用:

func hello(name: String) {
    print("Hello " + name)
}

hello(name: "Maria")
hello(name: "Vikram")

调用的时候必须要写明传入的参数是哪一个。

也可以像下面一样给参数定义一个外部名称,这样在调用的时候就必须通过外部名称来传入参数。

func hello(anotherName name: String) {
    print("Hello " + name)
}

hello(anotherName: "John")

如果想忽略外部参数名,可以将外部参数名指定为_,就可以不用外部参数名传入这个参数。例如:

func hello(_ name: String) {
    print("Hello " + name)
}

hello("John")

数组

  1. 由于Swift当中没有引用的概念,因此let声明的数组是真的常量数组,数组任何一个元素不允许变化。但是即使用var创建数组,数组元素的类型也不能改变了。

  2. 可以使用for xxx in xxxs来迭代遍历数组元素,需要注意和Python中只有函数作用域不同,Swift这里变量具有块级作用域,也就是说xxx这个变量只能在循环内使用,越过循环之后不能使用。对比Python这里,for i in range(3)在外面还是可以使用i变量的。

  3. 添加元素:

    • list.append(xx) 返回添加后的数组
    • list.insert(xx, at: 0) 返回添加后的数组
    • list += [1,2,3]如同python一样使用
  4. 删除元素:

    • list.remove(at: 2) 返回删除的元素
    • list.removeFirst() 返回删除的元素
    • list.removeLast() 返回删除的元素
    • list.removeAll() 无返回值
  5. list.count count属性得到数组当前大小

  6. 数组类型可以用类似于[Bool]的表达来指示类型

  7. 使用0 ... 5类似的形式表示0到5(包含两端)的范围,可以在for in中如同python的range一样使用。

字符串

  1. 可以在字符串里用\()来表示此处带入外部变量,形成格式化字符串。例如"hello, \(name)"

结构体

  1. 如果想修改结构体的字段,不仅要结构体中定义的该字段是var而不是let(变量),而且创建结构体实例的时候也要使用var而不是let。双重解锁才能更改结构体字段值。

  2. 计算属性:特殊的属性,取值时会根据其他字段或条件当场计算得出。有点类似vue里的计算量。计算属性必须用var声明。计算属性内部写法也是return,有点像简化后的函数。

  3. swift规定一切变量使用前都应该初始化。

  4. 在Swift中采用如下形式定义结构体:

    struct Foo {
        let size: Int
        var haha: String
    }
    
    let newFoo = Foo()
    

Enum 枚举 和 switch

enum LunchChoice {
    case pasta, burger
    case soup
}

var choice: LunchChoice
choice = .soup

为什么要将枚举和switch语句相提并论呢?因为swift中如果switch的是一个枚举类型,一定要在switch中包含所有情况:

func cookLunch(_ choice: LunchChoice) -> String {
    switch choice{
    case .pasta, .burger:
        return "no"
    case .soup:
        return "yes"
    }
}

如果不是枚举类型,则可以使用default来规定默认处理方法。

在枚举类型里面,可以用一个计算属性,包含switch self来得到自身其他内容:

enum LunchChoice {
    case pasta, burger
    case soup
    
    var price: Int {
        switch self{
        case .pasta, .soup: return 10
        case .burger: return 20
        }
    }
}

enum中也可以定义函数。在swift中,引用自身实例的属性,也可以在前面加一个self.