Swift入门-简洁版(1 / 3)

148 阅读10分钟

原视频:www.bilibili.com/video/BV1uZ…

Swift入门-简洁版(1 / 3)

Swift入门-简洁版(2 / 3)

Swift入门-简洁版(3 / 3)

一、学前须知

二、Swift简介

  • Swift是Apple在2014年6月WWDC发布的全新编程语言
  • Swift之父-Chris Lattner
    • Clang编译器的作者、LLVM项目的主要发起人
    • 从Apple离职后,跳槽到Tesla、Google
    • 目前(19年)在Google Brain从事AI研究
  • Swift5.x版本,ABI终于稳定
    • API(Application Programming Interface):应用程序编程接口
      • 源代码和库之间的接口
    • ABI(Application Binary Interface):应用程序二进制接口
      • 应用程序与操作系统之间的底层接口
      • 涉及的内容有:目标文件格式、数据类型的大小\布局\对齐、函数调用约定等等
  • 随着ABI的稳定,Swift语法不会有太大的改动
  • 完全开源,主要使用 C++ 编写

三、编译流程

  • 概览图

image.png

  • 编译流程

image.png

  • 参考:swift.org/compiler-st…

  • swiftc

    • swiftc存放在Xcode内部
      • Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin
    • 一些操作
      • 生成语法树:swiftc -dump-ast main.swift
      • 生成最简洁的SIL代码:swiftc -emit-sil main.swift(中间代码)
      • 生成LLVM IR代码:swiftc -emit-ir main.swift -o main.ll
      • 生成汇编代码:swiftc -emit-assembly main.swift -o main.s
    • 对汇编代码进行分析,可以真正掌握编程语言的本质

四、汇编初探

只是说明了一下Swift和OC之类的底层汇编不一样

五、HelloWorld

  • 一句代码即可执行

image.png

  • 不用辨析main函数,Swift将全局范围内的首句可执行代码作为程序入口

  • 一句代码尾部可以省略分号(;),多句代码写到同一行时必须用分号(;)隔开

  • var定义变量,let定义常量,编译器能自动推断出变量\常量的类型

    • 打印

image.png

  • 拼接打印

image.png

  • Playground可以快速预览代码效果,是学习语法的好帮手

  • 实时预览每行代码结果

image.png

  • Command + Shift + Enter:运行整个Playground

  • 可以选中只运行的部分

六、Playground - View

image.png

  • 还是UIKit框架
  • 展示vc或view都可以
  • 支持多Page

七、注释

  • 支持嵌套

image.png

  • Playground的注释支持Markup语法(与markdown相似)

  • //:开启单行Markup语法

  • /*:*/冒号后面开启多行Markup语法

  • 点击在Edit中的Show Render Markup来渲染markup效果

  • Markup语法只在Playground中有效

  • 支持页面切换:[下一页](@next)[上一页](@previous)

八、常量

  • 只能赋值1次

  • 其值不要求在编译时期确定(其他编程语言一般都会要求),但使用之前必须赋值1次

    • 如果不赋值,需要指定类型(推断:指定类型是在什么时期?) image.png
  • 常量、变量在初始化之前,都不能使用

image.png

九、标识符

  • 标识符(比如常量名、变量名、函数名)几乎可以使用任何字符

image.png

  • 标识符不能以数字开头,不能包含空白字符、制表符、箭头灯特殊字符

十、常见数据类型

  • 值类型(value type):

    • 枚举(enum):Optional

    • 结构体(struct):BoolIntFlaotDoubleCharacterStringArrayDictionarySet

      定义成结构体说明可以调用方法

  • 引用类型(reference type):

    • 类(class

十一、字面量

  • 布尔

    let bool = true // false
    
  • 字符串

    let string = "你好"
    
  • 字符(可存储ASCII字符、Unicode字符)

    let character: Character = "🐷"
    

    因为是双引号,如果不写Character,则会被认为是String类型

  • 整数

    let intDeciaml = 17 // 十进制
    let intBinary = 0b10001 // 二进制
    let intOctal = 0o21 // 八进制
    let intHexadecimal = 0x11 // 十六进制
    
  • 浮点数

    let doubleDecimal1 = 125.0 // 十进制,等价于1.25e2
    let doubleDecimal2 = 0.0125 // 十进制,等价于1.25e2
    
    let doubleHexadecimal1 = 0xFp2 // 十六进制,意味着15x2^2,相当于十进制的60.0
    let doubleHexadecimal2 = 0xFp-2 // 十六进制,意味着15x2^-2,相当于十进制的3.75
    //12.1875 == 1.21875e1 == 0xC.3p0
    
  • 整数和浮点数可以添加额外的下划线来增强可读性

    • 100_0000
    • 1_000_000.000_000_1
    • 000123.456
  • 数组

    let array = [1,3,5,7,9]
    
  • 字典

    let dictionary = ["age" : 18, "height" : 168 , "weight" : 120]
    
  • 类型转换

    不同类型不能直接相加(或其他计算\操作)

    • 整数转换

      let int1: UInt16 = 2_000
      let int2: UInt8 = 1
      let int3: int1 + UInt16(int2) 
      // 低类型要转换成高类型,即占用空间小的类型要转换成占用空间比较大的类型
      
    • 整数、浮点数转换

      let int = 3
      let double = 0.14159
      let pi = Double(int) + double
      let intPi = Int(pi)
      
    • 字面量可以直接相加,因为数字字面量本身没有明确的类型

      let result = 3 + 0.14159
      

      类型是给常量或变量定义的,和字面量没有关系(其实还有更深层次的原因,我猜是因为结构体)

可选类型(optionals)

使用可选类型optionals来处理值可能缺失的情况。可选类型表示两种可能: 或者有值, 你可以解析可选类型访问这个值, 或者根本没有值。

  • 可选类型

    let possibleNumber = "123"
    let convertedNumber = Int(possibleNumber)
    // convertedNumber 被推测为类型 "Int?", 或者类型 "optional Int"
    

    因为该构造器可能会失败,所以它返回一个可选类型(optional)Int,而不是一个 Int。一个可选的 Int 被写作 Int? 而不是 Int。问号暗示包含的值是可选类型,也就是说可能包含 Int 值也可能不包含值。(不能包含其他任何值比如 Bool 值或者 String 值。只能是 Int 或者什么都没有。)

  • nil

    你可以给可选变量赋值为 nil 来表示它没有值:

    var serverResponseCode: Int? = 404
    // serverResponseCode 包含一个可选的 Int 值 404
    serverResponseCode = nil
    // serverResponseCode 现在不包含值
    

    注意

    nil 不能用于非可选的常量和变量。如果你的代码中有常量或者变量需要处理值缺失的情况,请把它们声明成对应的可选类型。

    如果你声明一个可选常量或者变量但是没有赋值,它们会自动被设置为 nil

    var surveyAnswer: String?
    // surveyAnswer 被自动设置为 nil
    

    注意

    Swift 的 nil 和 Objective-C 中的 nil 并不一样。在 Objective-C 中,nil 是一个指向不存在对象的指针。在 Swift 中,nil 不是指针——它是一个确定的值,用来表示值缺失。任何类型的可选状态都可以被设置为 nil,不只是对象类型。

  • 空合运算符(Nil Coalescing Operator)

    • 空合运算符a ?? b)将对可选类型 a 进行空判断,如果 a 包含一个值就进行解包,否则就返回一个默认值 b。表达式 a 必须是 Optional 类型。默认值 b 的类型必须要和 a 存储值的类型保持一致。

    • 空合运算符是对以下代码的简短表达方法:

      a != nil ? a! : b
      

      上述代码使用了三元运算符。当可选类型 a 的值不为空时,进行强制解包(a!),访问 a 中的值;反之返回默认值 b。无疑空合运算符(??)提供了一种更为优雅的方式去封装条件判断和解包两种行为,显得简洁以及更具可读性。

      注意

      如果 a 为非空值(non-nil),那么值 b 将不会被计算。这也就是所谓的短路求值

    • 下文例子采用空合运算符,实现了在默认颜色名和可选自定义颜色名之间抉择:

      let defaultColorName = "red"
      var userDefinedColorName: String?   //默认值为 nil
      
      var colorNameToUse = userDefinedColorName ?? defaultColorName
      // userDefinedColorName 的值为空,所以 colorNameToUse 的值为 "red"
      
      userDefinedColorName = "green"
      colorNameToUse = userDefinedColorName ?? defaultColorName
      // userDefinedColorName 非空,因此 colorNameToUse 的值为 "green"
      
  • 强制解析

    当你确定可选类型确实包含值之后,你可以在可选的名字后面加一个感叹号(!)来获取值。这个惊叹号表示“我知道这个可选有值,请使用它。”这被称为可选值的强制解析(forced unwrapping)

    if convertedNumber != nil {
        print("convertedNumber has an integer value of \(convertedNumber!).")
    }
    // 输出“convertedNumber has an integer value of 123.”
    

    注意

    使用 ! 来获取一个不存在的可选值会导致运行时错误。使用 ! 来强制解析值之前,一定要确定可选包含一个非 nil 的值。

  • 隐式解析可选类型

    • 在程序架构中,第一次被赋值之后,可以确定一个可选类型总会有值。这种类型的可选状态被定义为隐式解析可选类型(implicitly unwrapped optionals)

    • 把想要用作可选的类型的后面的问号(String?)改成感叹号(String!)来声明一个隐式解析可选类型。

    • 与其在使用时把感叹号放在可选类型的名称的后面,你可以在定义它时,直接把感叹号放在可选类型的后面。

    • 当可选类型被第一次赋值之后就可以确定之后一直有值的时候,隐式解析可选类型非常有用。

    • 隐式解析可选类型主要被用在 Swift 中类的构造过程中,请参考 无主引用以及隐式解析可选属性

    • 一个隐式解析可选类型其实就是一个普通的可选类型,但是可以被当做非可选类型来使用,并不需要每次都使用解析来获取可选值。

    • 下面的例子展示了可选类型 String 和隐式解析可选类型 String 之间的区别:

      let possibleString: String? = "An optional string."
      let forcedString: String = possibleString! // 需要感叹号来获取值
      
      let assumedString: String! = "An implicitly unwrapped optional string."
      let implicitString: String = assumedString  // 不需要感叹号
      
    • 你可以把隐式解析可选类型当做普通可选类型来判断它是否包含值:

      if assumedString != nil {
          print(assumedString!)
      }
      // 输出“An implicitly unwrapped optional string.”
      
    • 你也可以在可选绑定中使用隐式解析可选类型来检查并解析它的值:

      if let definiteString = assumedString {
          print(definiteString)
      }
      // 输出“An implicitly unwrapped optional string.”
      

    注意

    如果一个变量之后可能变成 nil 的话请不要使用隐式解析可选类型。如果你需要在变量的生命周期中判断是否是 nil 的话,请使用普通可选类型。

十二、元组(tuple)

let http404Error = (404, "Not Found")
print("The status code is \(http404Error.0)")
let (statusCode, statusMessage) = http404Error
print("The status code is \(statusCode)")
let (justTheStatusCode,_) = http404Error
let http200Status = (statusCode: 200, description: "OK")
print(The status code is \(http200Status.statusCode))

十三、流程控制

1、if-else

let age = 4
if age >= 22 
{
  print("Get married")
} 
else if age >= 18
{
  print("Being a adult")
}
else if age >= 7
{
  print("Go to school")
}
else
{
  print("Just a child")
}
  • if后面的条件可以省略小括号
  • 条件后面的大括号不可以省略
  • if后面的条件只能是Bool类型(不能是intnil等)

2、while

var num = 5
while num > 0
{
  print("num is \(num)")
  num -= 1
} // 打印了5次
  • 同样小括号可以省略,大括号不能省略

  • 为什么是num -= 1而不是num--

    从Swift3开始,去除了自增(++)、自减(--)运算符,因为不易阅读

var num = -1
repeat
{
  print("num is \(num)")
} while num > 0 // 打印了1次
  • repeat-while相当于C语言中的do-while

3、for

  • 闭区间运算符:a...b,即:a <= 取值 <= b

    let names = ["Anna", "Alex", "Brian", "Jack"]
    for i in 0...3
    {
      print(names[i])
    }
    // Anna Alex Brian Jack
    
    let range = 1...3
    for i in range 
    {
      print(names[i])
    }
    // Alex Brian Jack
    
    let a = 1
    var b = 2
    for i in a...b
    {
      print(names[i])
    }
    // Alex Brian
    
    for i in a...3
    {
      print(names[i])
    }
    // Alex Brian Jack
    
    // i默认是let,有需要时可以声明为var
    for var i in 1...3
    {
      i += 5
      print(i)
    }
    // 6 7 8
    
    • i默认是let
    for _ in 1...3
    {
      print("for")
    }
    // 打印了3次
    
  • 半开区间运算符:a..<b,即:a <= 取值 < b

  • for - 区间运算符用在数组上

    let names = ["Anna", "Alex", "Brian", "Jack"]
    for name in names[0...3]
    {
      print(name)
    }
    // Anna Alex Brian Jack
    
  • 单侧区间:让区间朝一个方向尽可能的远

    for name in names[2...]
    {
      print(name)
    }
    //  Brian Jack
    
    for name in names[...2]
    {
      print(name)
    }
    // Anna Alex Brian
    
    for name in names[..<2]
    {
      print(name)
    }
    // Anna Alex
    
    let range = ...5
    range.contains(7) // false
    range.contains(4) // true
    range.contains(-3) // true
    
  • 区间类型

    let range1: ClosedRange<Int> = 1...3
    let range2: Range<Int> = 1..<3
    let range3: PartialRangeThrough<Int> = ...5
    
  • 字符、字符串也能使用区间运算符,但默认不能用在for-in中:

    // 错误举例:
    for _ in "a"..."z"
    {
      print("123")
    }
    // 会报错
    

    但是可以单独拿出来用:

    let stringRange1 = "cc"..."ff" // ClosedRange<String>
    stringRange1.contains("cb") // false
    stringRange1.contains("dz") // true
    stringRange1.contains("fg") // false
    
    let stringRange2 = "a"..."f" // 此处仍是字符串ClosedRange<String>
    stringRange2.contains("d") // true
    stringRange2.contains("h") // false
    
    // \0到~囊括了所有可能要用到的ASCII字符
    let characterRange: ClosedRange<Character> = "\0"..."~"
    characterRaneg.contains("G") // true
    
  • 带间隔的区间值 - stride

    let hours = 11
    let hourInterval = 2
    // tickMark的取值:从4开始,累加2,不超过11
    for tickMark in stride (from: 4, through: hours, by: hourInterval)
    {
      print(tickMark)
    } // 4 6 8 10
    

4、switch

var number = 1
switch number
{
  case 1:
  		print("number is 1")
      break
  case 2:
  		print("number is 2")
      break
  default:
  		print("number is other")
  		break
}
  • casedefault后面不能写大括号{}

  • 默认可以不写break,并不会贯穿到后面的条件

  • 使用fallthrough可以实现贯穿效果

  • switch必须要保证能处理所有情况 image.png

  • casedefault后面至少有一条语句

    如果不想做任何事,加个break即可 image.png

  • 如果能保证已处理所有情况,也可以不必使用default image.png

  • 复合条件

    • switch也支持Character、String类型

image.png

  • 不需要fallthrough,用,也可以

image.png

  • 区间匹配、元组匹配 image.png

  • 下划线_代表:忽略某个值

  • 关于case匹配问题,属于模式匹配(Pattern Matching)的范畴,以后会再次详细展开讲解

  • 值绑定

image.png

  • 带有值绑定,只需要一方匹配

  • 必要时let也可以改为var

  • where

image.png

  • for循环中作用类似oc的continue

  • 标签语句

image.png