前言
假设您有 Objective-C 基础,想入门 Swift,我这里做了一个简要教程,列举出了 Objective-C 和 Swift 的主要区别。您可以根据这部分自己再去发散。
从入门到精通就是这么简单!
ABI
ABI(Application Binary Interface):应用程序二进制接口
√应用程序与操作系统之间的底层接口
√涉及的内容有:目标文件格式、数据类型的大小布局对齐、函数调用约定等等
编译流程
小试牛刀
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
注释
支持 markdown
//单行注释冒号紧跟//
//:
//多行注释冒号在第二行
/*:
*/
数据类型
值类型/引用类型
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)