Swift语言简介
Swift是在Objective-C语言的基础上发展而来的一门现代高级语言。由苹果公司于2014年6月推出,目前已经是2.0版本。
Swift与Cocoa和Cocoa Touch框架高度集成,支持开发Mac OS X、iOS、watchOS应用。基于Objective-C运行时,支持与Objective-C语言双向互操作。
Swift语言有三种开发方式
Playground:交互式编写代码,适合快速学习、测试、可视化观察
REPL(Read-Eval-Print-Loop)命名行:生成.swift文件之后在在命名行下键入 xcrun swiftc 文件名.swift 来编译运行。适合调试、研究、微观探查。
Xcode项目:构建正规工程项目,使用大型框架,追求设计质量与代码组织。
Swift的编译过程
相较于Objective-C的编译过程,在前端和LLVM IR优化之间多了一步叫SIL Optimizer(Swift中间语言优化)的步骤。
类型系统
值类型 value type有:
基础数值类型
结构 struct
枚举 enum
元组 tuple
特殊值类型有:
字符串 string
数组 Array
字典 Dictionary
集 Set
引用类型 reference type
类 class
闭包 closure
类型装饰
协议 protocol
扩展 extension
泛型 generics
基础数值类型
学习资料
**Swift官方资源 **https://developer.apple.com/swift/resources/
值类型与引用类型
类型成员Type Member
属性 property:数据成员 data member,描述对象状态
方法 method:函数成员 function member,描述对象行为
初始化器 init
析构器 deinit
下标 subscript
class MyClass {
//属性
var x:Int
var y:Int
var datas:[Int]=[1,2,3,4,5]
//初始化器
init(x:Int, y:Int){
self.x=x
self.y=y
}
//方法
func print(){
println("\(x), \(y), \(datas)")
}
//下标
subscript(index: Int) -> Int {
get {
return datas[index]
}
set(newValue) {
datas[index]=newValue
}
}
//析构器
deinit{
println("clear up resources")
}
}
类与结构class and struct
类的实例叫对象(object),是一个引用类型,在栈上就是一个指针,指向一个位于堆上的实体对象。
结构的实例叫值(value),是一个值类型,实例直接位于栈中。
对象和值再不同的内存空间(栈和堆)拷贝和传参过程分析
class RPoint{
var x:Int
var y:Int
init(x:Int, y:Int){
self.x=x
self.y=y
}
}
struct SPoint{
var x:Int
var y:Int
init(x:Int, y:Int){
self.x=x
self.y=y
}
}
var rp=RPoint(x:10,y:20)
var sp=SPoint(x:10,y:20)
func foo(){
var rp1=RPoint(x:10,y:20)
var rp2=rp1
var sp1=SPoint(x:10,y:20)
var sp2=sp1
rp1.x++
rp1.y++
sp1.x++
sp1.y++
}
func foo1(){
var rp1=RPoint(x:10,y:20)
var sp1=SPoint(x:10,y:20)
foo2(rp1,sp1)
}
func foo2(var rp2: RPoint, var sp2: SPoint){
rp2.x++
rp2.y++
sp2.x++
sp2.y++
}
foo()
foo1()
根据以上代码运行后,foo函数的内存模型如下,sp1是value,经过复制sp2操作后,sp1和sp2都是value,存储在栈区域内。
rp1是object,复制rp2操作后,只是在栈区域复制了一个地址rp2,指向的还是和rp1同一块堆空间。
根据以上代码运行后,foo1和foo2函数的内存模型如下,当一个value(sp1)做为参数传递时,接收的函数将value的值再复制一份到自己的栈中,当object(sp1)做为参数传递时,接收的函数只复制一份相同的地址存到自己的栈中,并且指向同一块堆空间。
类型成员:属性
属性用来表达实例状态或类型状态。
属性按类别可以分为存储属性和计算属性,按照可变性可分为变量属性和常量属性,根据归属权可以分为实例属性和类型属性(静态属性)
为了更好的的理解属性的本质,我们要学会查看sil中间格式,在命令行输入
xcrun swift -emit-silgen point.swift -o point.sil
这样就生成了一个point.sil文件,可以通过文件编辑器查看其中的代码。
存储属性的本质构成是一个存储变量和两个访问器方法(ge方法和set方法)
计算属性没有一个存储的变量,但是他也有两个访问器方法,也可以简化为一个get方法。
struct SPoint{
var x:Int
var y:Int
}
class RPoint{
var x:Int
var y:Int
static var max=100 //静态变量属性
init(x:Int,y:Int){
self.x=x
self.y=y
}
}
var sp=SPoint(x:10,y:20)
var rp=RPoint(x:10,y:20)
struct SCircle{
var center:RPoint
var radius:Int
}
class RCircle{
var center:RPoint
var radius:Int
init(center:RPoint, radius:Int){
self.center=center
self.radius=radius
}
static var min=RPoint(x:0,y:0)
}
var sc=SCircle(center:rp, radius:10)
var rc=RCircle(center:rp, radius:100)
存储属性的生存周期
实例存储属性的生命周期跟随实例本身,实例销毁了实例存储属性也一同销毁。
静态存储属性的值存储在静态数据区,生命周期从类型加载开始,到类型卸载结束(生存周期非常长)。
值存储属性直接“内嵌”在实例中。
引用存储属性通过指针“强引用”堆上的引用类型实例,ARC系统会对引用数进行管理,当引用数为0时销毁。
Lazy存储属性:如果有的类型对象占用内存空间很大,可以使用Lazy属性延迟初值计算到访问时。
class DataProcessor{
lazy var myData=LargeData()
}
属性初始化:类中所有的属性必须初始化,要么提供默认值 或者 构造器初始化。结构中的存储属性可以不用程序员写初始化器,编译器会自动帮我们添加构造方法。
属性观察者:检测改变
willSet 在改变前调用
didSet 在改变后调用
class DataStorage {
var data: Int = 0 {
willSet {
print("About to set data to \(newValue)")
}
didSet {
print("Changed from \(oldValue) to \(data)")
}
}
}
类型成员:方法
函数:代码段上的可执行指令序列
全局函数
func add(data1:Int, data2:Int)-> Int{
return data1+data2
}
成员函数
成员函数也叫方法,方法是类型的成员函数,表达行为,方法可以定义与以下类型:class,struct,enum
class MyClass {
var instanceData=100
static var typeData=10000
//实例方法
func instanceMethod(){
++instanceData
++MyClass.typeData
instanceMethod2()
MyClass.typeMethod()
}
//静态方法
static func typeMethod(){
++typeData
//++instanceData
typeMethod2()
//instanceMethod()
}
}
归属权
实例方法-反应的是一个实例行为,可以访问实例方法与属性、以及类型属性与方法。
类型方法-类型行为,只能访问类型属性与方法。
方法参数
func(参数1,参数2...)->返回值{ }
参数形式:外部参数名 本地参数名:类型
声明时可以省略外部参数名,这时外部参数名使用本地参数名。
调用时,第一个参数名可忽略,但后面的参数名必须显示标明。如果在声明时加_,调用时也可以忽略参数名
方法可以没有参数,也可以没有返回值
参数传递默认为传值方式
//显式内部参数名,显式外部参数名
func sayGreeting(person name:String,greeting words:String)->String{
return words+"! "+name
}
//调用显式内部参数名,显式外部参数名函数
myObject.sayGreeting(person:"Jason", greeting:"You are welcome")
//显式内部参数名,省略外部参数名
func sayGreeting(name:String, _ words:String)->String{
return words+"! "+name
}
//调用显式内部参数名,省略外部参数名函数,注意如果上面的_ words没有写下划线,就不能省略第二个内部参数名words
myObject.sayGreeting("Jason","Welcome")
//无参数,无返回值
func sayGreeting(){
print("Hello!")
}
更多参数与返回值功能
提供参数默认值
//设置参数的默认值为Jason
func sayGreeting(name:String="Jason")->String {
return "Hello! "+name
}
常量参数 VS 变量参数
//data1是常量参数 data2是变量参数
func changeParameter(data1:Int, var data2:Int){
var dataTemp=data1
data2++
dataTemp++
}
可变数目参数:实质上传递的是数组
//可变数目参数
func averageNumber(numbers:Double...)->Double{
var sum=0.0
for item in numbers {
sum+=item
}
return sum / Double(numbers.count)
}
//可变参数个数函数的调用
myObject.averageNumber(10,90,30,80,50,100)
inout参数,可以改变外部实参,注意与引用类型参数区别
//inout参数
func swap(inout a:Int,inout b:Int){
let tempA=a
a=b
b=tempA
}
//调用inout参数的函数
myObject.swap(&data1,b: &data2)
多个返回值:Tuple类型
//返回Tuple类型
func minMax(array: [Int]) -> (min: Int, max: Int){
var currentMin = array[0]
var currentMax = array[0]
for value in array[1..<array.count] {
if value < currentMin {
currentMin = value
} else if value > currentMax {
currentMax = value
}
}
return (currentMin, currentMax)
}
//返回Tuple类型的函数调用
let range=myObject.minMax([3,-9,23,15,-45,7])
print("max=\(range.max), min=\(range.min)")
类型成员:初始化器
认识初始化器
初始化器用于初始化类型实例,是一个特殊的函数,无返回值。初始化器以init开头
//直接赋初值的初始化器
init(){
x=10
y=10
}
//自定义初值的初始化器
init(x:Int, y:Int){
self.x=x
self.y=y
print("init is invoked")
}
var pt1=Point()
//调用初始化器,初始化器的第一个参数名也不能省略
var pt2=Point(x: 100,y:200)
类型实例的初始化过程
1.分配内存
2.调用舒适化器初始化内存
初始化器可以定义与以下类型:class, struct, enum
只有实例初始化器,没有类型初始化器
初始化实例属性
初始化器可以指定默认值,也可以自定义初始值。所有存储属性必须被初始化,实例存储属性可以使用初始化器初始化,类型存储属性不可以使用初始化器,所以必须指定默认值。
可选属性类型可以不初始化
//可选属性类型初始化时要在后面加上一个问号‘?’,实际上是编译器把数值初始化成了nil
class Product{
var no:Int?
var description: String?
}
存储属性被初始化时,不会调用属性观察者。
默认初始化器
一个类可以有多个初始化器,但至少需要一个初始化器。如果一个类没有提供初始化器,那么编译器会自动生成一个默认的初始化器,默认的初始化器是一个无参数的初始化器init()。
指定初始化器 VS 便捷初始化器
指定初始化器(Designated Initializer)为类的主初始化器,负责初始化所有属性。必须调用其父类的主初始化器。
便捷舒适化器(Convenience Initializer)为类的辅助初始化器。必须调用同类的指定初始化器。
class Point3D{
var x:Int
var y:Int
var z:Int
//指定初始化器,也叫主初始化器
init(x:Int, y:Int, z:Int){
self.x=x
self.y=y
self.z=z
//other processing
print("other task")
}
//便捷初始化器,要加convenience关键字在函数前,函数内部就可以调用指定初始化器了
convenience init(x:Int, y:Int){
self.init(x:x,y:y,z:0)
}
convenience init(){
self.init(x:0,y:0,z:0)
}
}
类型成员:析构器
认识析构器
析构器(deinit)在实例内存被释放前调用,用于释放实例使用的非内存资源。 析构器仅可以定义于class,且只能定义一个。struct和enum不能定义析构器。
归属权:只有实例析构器,没有类型析构器
析构器的调用是由运行时根据ARC的释放规则调用,程序员无法手工调用。
class FileStream{
init(){
print("open file...")
}
func process(){
print("process file...")
}
//deinit是析构器,由于析构器是一个特殊的函数,所以之前不需要加func,也不需要加参数
deinit{
print("close file...")
}
}
var fs:FileStream?
fs=FileStream() //引用计数:1
fs!.process()
fs = nil //引用计数:0 ARC在这时将自动调用析构器
ARC简介
ARC(Automatic Reference Counting)自动引用计数,用于管理堆上的对象实例所分配的动态内存。在Swift语言中必须使用ARC管理机制,不可以关闭ARC来手动管理。ARC不负责管理栈上的内存。栈上的内存有运行时根据函数生存周期来自动管理栈的创建和销毁。ARC通过追踪“对象倍引用的计数”来确定对象是否还需要被访问,如果对象的引用计数为0,ARC会立即调用析构器,并随后释放对象内存。
引用计数管理
对象引用计数增加1:
新创建对象并赋值给变量
var fs:FileStream?
fs=FileStream() //引用计数:1
将对象引用拷贝给其他变量或常量
var fs2 = fs //引用计数:+1(等于2)
将对象赋值给其他属性(无论实例属性、还是类型属性)
class MyClass{
var myFile:FileStream?
init(myFile: FileStream?){
self.myFile=myFile
}
}
var mc: MyClass?
mc = MyClass(myFile:nil)
mc!.myFile=fs //引用计数:3
将对象传递给函数参数(非inout参数),或者返回值
func invoke(file: FileStream?){
file!.process()
}
//调用时引用计数+1 : 4
invoke(fs)
//函数结束引用计数-1 : 3
对象引用计数减1:
将变量赋值为nil
fs = nil
将属性赋值为nil,或者属性所在的对象被释放(实例属性)
mc!.meFile = nil 或者 mc = nil
传值参数离开函数
下标与操作符
下标(Subscripts)
下标支持使用索引的方式访问“集合式”实例,例如vector[index]
下标可以定义于class, struct, enum
下标可以类比为“含参的计算属性”,其本质是一对代索引参数的访问器方法(get/set)
访问下标
归属权:只能定义实例下标,不能定义类型下标(静态下标)
可以定义读写下标(get和set),或者只读下标(get)
下标的索引参数可以是任意类型,也可以设计多个参数
一个类型可以提供多个下标的重载版本(参数不同)
class Vector{
var datas = [Int](count:100,repeatedValue:0)
subscript(index:Int)-> Int{
get{
return datas[index]
}
set{
datas[index]=newValue
}
}
}
var datas=Vector()
for i in 0..<10{
datas[i]=i
}
for i in 0..<10{
print(datas[i])
}
标准操作符
赋值操作符:= 不返回结果
算术操作符:+ - * / % 检测溢出错误
自增、自减操作符:++ --
复合赋值操作符:+= -=
比较操作符:> < >= <=
罗辑操作符:&& || !
位操作符:~ & | ^ << >>
三元操作符:a ? b : c
范围操作符:a..<b(a到小于b) a...b(a到b)
值相等:== !=
引用相等:=== !==
重载操作符
除了标准操作符之外,还可以针对自定义类型重载标准操作符。重载操作符又称“操作符函数”,其本质是全局函数。
可以重载前缀(prefix)、中缀(infix)、后缀(postfix)操作符。前缀和后缀操作符需要加关键词:prefix 或 postfix
也可以通过将参数设置为inout参数,重载复合赋值操作符,例如+=, -=
除了重载Swift标准操作符,也可以自定义新的操作符,并重载。
//中缀操作符
func + (left: Complex, right: Complex) -> Complex {
return Complex(real: left.real + right.real,
imag: left.imag + right.imag)
}
//前缀操作符
prefix func - (data: Complex) -> Complex {
return Complex(real: -data.real, imag: -data.imag)
}
//复合赋值操作符
func += (inout left: Complex, right: Complex) {
left = left + right
}
//自定义操作符
prefix operator +++ {}
prefix func +++ (inout data: Complex) -> Complex {
data += data
return data
}
相等操作符
引用相等:判断对象地址是否相等,仅针对引用类型,不适用于值类型。相等操作符===,不相等操作符!==
值相等:判断实例值是否相等,自定义类型需要重载提供“值比较语义”。相等操作符==,不相等操作符!=