前言

假设您有 Objective-C 基础,想入门 Swift,我这里做了一个简要教程,列举出了 Objective-C 和 Swift 的主要区别。您可以根据这部分自己再去发散。
从入门到精通就是这么简单!

ABI

ABI(Application Binary Interface):应用程序二进制接口
√应用程序与操作系统之间的底层接口
√涉及的内容有:目标文件格式、数据类型的大小布局对齐、函数调用约定等等

编译流程

编译流程1

编译流程2

小试牛刀

swiftc 位置:/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin
(和 clang 位于同一个目录)

用法:

swiftc -help

更多

口生成语法树: 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

Playground

playground

playground

注释

支持 markdown

//单行注释冒号紧跟//

//:

//多行注释冒号在第二行

/*:

*/

数据类型

值类型/引用类型

playground

Int

Int 是结构体,优点:可以自带方法/属性,比如:Int.max

元祖(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)")

流程控制

if后面的条件只能是 Bool类型

if else

没有自增自减

闭区间运算符(closed range)

a...b
举例:

let names = ["Anna","Alex","Brian","Jack"]
for i in 0....3 {
  print(names[i])
}// Anna Alex Brian Jack

单侧区间

for i in [2...] {
  print(names[i])
}// Anna Alex Brian Jack

switch case

  • case、default后面不能写大括号{}
  • 默认可以不写break,并不会贯穿到后面的条件
  • fallthrough实现贯穿效果
  • 支持区间匹配、元祖匹配

函数

返回元祖:实现多返回值

参数标签(Argument Label )

func goTowork(at time: string){
    print("this time is (time)")
}
goToWork(at:"88:06")// this time is 08:00

可以使用下划线 _ 省略参数标签

输入输出参数(In-Out Parameter )

可以用inout定义一个输入输出参数:可以在函数内部修改外部实参的值

func swapValues(_v1:inout Int,_v2: inout Int){
    let tmp= v1
    v1 = v2
    y2 = tmp
}
var numl = 10
var num2 = 20
swapValues(&num1,&num2)

推荐使用元祖:

func swapValues(vl:inout Int,v2: inout Int){
    (v1,v2)=(v2,v1)
}

函数重载

  • 函数名相同,但参数个数不同
  • 函数名相同,但参数类型不同
  • 函数名相同,但参数标签不同

内联函数

用于编译器优化:

func test(){
 print("test")
}
//优化成:
print("test")

不会内联的情况

  • 函数体很长
  • 包含了递归调用
  • 动态派发

不想被内联

@inline(never)func test(){
    print("test")
}

//开启编译器优化后,即使代码很长,也会被内联(递归调用函数、动态派发的函数除外)
@inline( always)func test(){
    prin{("test")
}

函数可以作为参数传递

  • 函数类型
  • 函数可以作为参数

返回值是函数类型的函数,叫做高阶函数(Higher-Order Function )

typealias

typealias用来给类型起别名

typealias Byte = Int8
typealias Short = Int16
typealias Long = Int64

按照Swift标准库的定义,Void就是空元组()

public typealias Void =()

枚举

关联值

关联值

原始值

enum PokerSuit : Character {
 case spade = "♠️"
 case heart = "♥️"
 case diamond = "♦️"
 case club = "♣️"
}

var suit= PokerSuit.spade
print(suit)// spade
print(suit.rawValue)//♠️
print(PokerSuit.club.rawValue)//♣️

隐式原始值(Implicitly Assigned Raw Values )

■如果枚举的原始值类型是Int、string,Swift会自动分配原始值

递归枚举(Recursive Enumeration)

MemoryLayout

和 sizeof 作用相同

var age = 10
MemoryLayout<Int>.size

猜测输出什么:

enum Password {
    case number(Int, Int, Int, Int)
    case other
}
MemoryLayout<Password>,stride // 40,分配占用的空间大小
MemoryLayout<Password>.size //33,实际用到的空间大小
MemoryLayout<Password>.alignment //8,对齐参数

关联值和原始值的内存占用

原始值的内存占用比较复杂,并不是简单的累加。这部分需要注意。

可选项(Optional)

  • 可选项,一般也叫可选类型,它允许将值设置为nil
  • 在类型名称后面加个问号? 来定义一个可选项

强制解包(Forced Unwrapping )

关联值

解决方案:

let number = Int("123")
if number != nil{
    print("字符串转换整数成功:\(number!)")
} else {
    print("字符串转换整数失败")
}

//字符串转换整数成功:123

可选项绑定(Optional Binding)

  • 可以使用可选项绑定来判断可选项是否包含值
  • 如果包含就自动解包,把值赋给一个临时的常量(let)或者变量(var),并返回true,否则返回false
if let number = Int("123") {
    print("字符串转换整数成功:\(number)")
    // number是强制解包之后的Int值
    // number作用域仅限于这个大括号
} else {
    print("字符串转换整数失败")
}

// 字符串转换整数成功:123

等价写法

if let first = Int("4") {
    if let second = Int("42") {
        if first < second && second < 100 {
            print("\(first) < \(second) < 100")
        }
    }
}
// 4 < 42 < 100

等于

if let first = Int("4"),
    let second = Int("42"),
    first < second && second < 100 {
        print("\(second) < \(second) < 100")
}
// 4 < 42 < 100

空合并运算符 ??(Nil-Coalescing Operator)

guard 语句

  • 当guard语句的条件为false时,就会执行大括号里面的代码
  • 当guard语句的条件为true时,就会跳过guard语句
  • guard语句特别适合用来“提前退出
  • 使用guard语句进行可选项绑定时,绑定的常量(let)、变量(var)也能在外层作用域中使用

隐式解包(Implicitly Unwrapped Optional)

  • 在某些情况下,可选项一旦被设定值之后,就会一直拥有值
  • 在这种情况下,可以去掉检查,也不必每次访问的时候都进行解包,因为它能确定每次访问的时候都有值
  • 可以在类型后面加个感叹号 ! 定义一个隐式解包的可选项

多重可选项

var num1: Int? = 10
var num2: Int?? = num1
var num3: Int?? = 10

图解:

关联值

当然,也可以使用lldb指令 frame variable –R 或者 fr v –R 查看区别

关联值

高级知识

窥探内存

查看内存方式

使用举例:查看 enum 类型的内存布局

查看内存方式

高高低低:高内存在高字节,低内存在低字节。

汇编

常见命令

call 命令表示函数调用,后面跟上的是函数地址。
lea 地址传递指令
% 后面跟寄存器
$ 后面跟立即数
()用于找到存储空间

测试

验证 inout 是地址传递,而非值传递

var number
func test( num: inout Int){
    num = 20
}
test(&number)