介绍:
Swift是苹果2014年推出的全新编程语言,它继承了C语言、ObjC的特性,且克服了C语言的兼容性问题。Swift发展过程不仅保留了Objc很多语言特性,他也借鉴了多种现代化语言的特点,在其中可以看到C#、Java、JavaScript、Python等多种语言的影子。同时在2015年WWDC上苹果还宣布了Swift的新版本Swift2.0,Swift是开源的,还支持iOS、OS X、Linux。
Swift2.0官方中文版+100个必备tips:http://download.csdn.net/detail/shaoting19910730/9189677
基础部分:
第一个Swift程序:
1 import Foundation 2 3 /** 4 * Swift没有main函数,默认从top level code的上方开始自上而下执行(因此不能有多个top level代码) 5 */ 6 println("Hello, World!")
从上面得代码可以看出:
1、Swift没有main函数,从top level code的上方开始往下执行 ,即第一个非声明语句开始执行(表达式、控制结构、类、结构体、枚举、方法等属于声明语句),不能存在多个top level code文件,否则编译器不能确定执行入口,事实上swift隐含了一个main函数,这个main函数会设置并且调用全局“C_ARGC C_ARGV”并调用由top level code构成的top_level_code()函数。
2、Swift通过import引入其他类库。
3、Swift语句不需要双引号结尾,除非一行之中有多个语句。
数据类型
Swift包含了C和Objc语句中的所有基础类型:Int整形、Float和Double浮点型、Bool布尔型、Character字符型、String字符串类型、enum枚举、struct结构体构造类型、Array数组、Set集合、Dictionary字典集合类型,不仅如此还增加了高阶数据类型:元组(Tuple),可选类型(Optinal)。
基础类型
Xcode从6.0开始加入了Playground代码测试,可以实时查看代码执行结果,下面使用Playground简单演示一下Swift的基础内容,对Swift有个简单的认识:
1 import Foundation 2 3 var a:Int=1 //通过var定义一个变量 4 5 //下面变量b虽然没有声明类型,但是会自动进行类型推断,这里b推断为Int类型 6 var b=2 7 8 var c:UInt=3 9 let d=a+b //通过let定义一个常量 10 11 //下面通过"\()"实现了字符串和变量相加(字符串插值),等价于println("d="+String(d)) 12 println("d=\(d)") //结果:d=3 13 14 //注意由于Swift是强类型语言,a是Int类型而c是UInt类型,二者不能运算,下面的语句报错;但是注意如果是类似于:let a=1+2.0是不会报错的,因为两个都是字面量,Swift会首先计算出结果再推断a的类型 15 //let e=a+c 16 17 //Int.max是Int类型的最大值,类似还有Int.min、Int32.max、Int32.min等 18 let e=Int.max //结果:9223372036854775807 19 20 var f:Float=1.0 21 var g=2.0 //浮点型自动推断为Double类型 22 23 var h:String="hello " 24 25 //emoj表情也可以作为变量或者常量,事实上所有Unicode字符都是可以的 26 var 💖🍎="love and apple" 27 28 //两个字符串相加,但是注意不同类型不能相加 29 var i=h+💖🍎 //结果:hello love and apple 30 31 //布尔类型只有两个值true、false,类似于if语句中的条件只能是布尔类型不能像ObjC一样非0即真 32 var j:Bool=true 33 34 //字符类型,同样使用双引号,但是只能是一个字符,如果不指定类型则"c"默认会推断为字符串(var k:Character="c"是字符类型,但是var k="c"是字符串类型) 35 var k:Character="c" 36 37 var l=00100 //等于100,可以在前面添加额外的0 38 var m=10_000_000 //等于10000000,可以使用增加额外的下划线方便阅读而不改变值的大小
1、Swift通过var进行变量定义,通过let进行常量定义。
2、Swift添加了类型推断,对于赋值的常量或者变量会自动推断其具体类型。
3、Swift是强类型语言,不同的数据类型之间不能隐式转换,如果需要转换只能强制转换。
4、在Swift中类型转换直接通过其类型构造函数即可,降低了API的学习成本。
集合类型:
Swift提供了三种集合类型:数组Array、集合Set、字典Dictionary。和Objc不同的是,由于Swift的强类型,集合中的元素必须是同一类型,而不能像Objc一样可以储存任何对象类型,并且注意Swift中的集合是值类型二非引用类型(string、结构体struct、枚举enum都是值类型)。
首先看一下Swift中的数组:
1 //声明数组的时候必须确定其类型,下面使用[String]声明一个字符串数组([String]是Array<String>简单表达形式) 2 //var a:Array<String>=["hello","world"] 3 var a:[String]=["hello","world"] 4 a[0] //访问数组元素 5 6 //下面创建一个Double类型的数组,这里没有使用字面量,当前是一个空数组,当然也可以写成var b:[Double]=[] 7 var b=[Double]() 8 9 for i in a{ 10 println("i=\(i)") 11 } 12 13 //添加元素,Swift中可变类型不再由单独的一个类型来表示,统统使用Array,如果想声明为不可变数组只要使用let定义即可 14 a.append("!") 15 16 a+=["I" ,"am" ,"shaoting"] //追加元素 17 18 println("a.count=\(a.count)") //结果:a.count=6 19 20 a[3...5]=["I","Love","Swift"] //修改元素,但是注意无法用这种方式添加元素 21 //a[6]=["."]//这种方式是错误的 22 23 a.insert("New", atIndex: 5) //插入元素:hello world! I Love New Swift 24 25 a.removeAtIndex(5) //删除指定元素 26 27 //使用全局enumerate函数遍历数据索引和元素 28 for (index,element) in enumerate(a){ 29 println("index=\(index),element=\(element)") 30 } 31 32 //使用构造函数限制数组元素个数并且指定默认值,等价于var c=Array(count: 3, repeatedValue: 1),自动推断类型 33 var c=[Int](count: 3, repeatedValue: 1)
Set表示没有顺序的集合:
1 //注意集合没有类似于数组的简化形式,例如不能写成var a:[String]=["hello","world"] 2 var a:Set<String>=["hello","world"] 3 var b:Set=[1,2] //类型推断:Set<Int> 4 5 a.insert("!") //注意这个插入不保证顺序 6 7 if !a.isEmpty { //判断是否为空 8 a.remove("!") 9 } 10 11 if !a.contains("!"){ 12 a.insert("!") 13 }
DIctionary字典同样是没有顺序的,并且在Swift中字典同样要在使用时明确具体的类型。和Objc中一样,字典必须保证key是唯一的,而这一点就要求在Swift中的key必须是可哈希的,不过幸运的是Swift中的基本类型(如Int、Float、Double、Bool、String)都是可哈希的,都可以作为key。
1 //通过字面量进行字典初始化,注意等价于var a:Dictionary<Int,String>=[200:"success",404:"not found"] 2 var a:[Int:String]=[200:"success",404:"not found"] 3 var b=[200:"success",404:"not found"] //不声明类型,根据值自动推断类型 4 5 a[200] //读取字典 6 a[404]="can not found" //修改 7 8 a[500]="internal server error" //添加 9 //a=[:] //设置为空字典,等价于:a=[Int:String]() 10 11 for code in a.keys{ 12 println("code=\(code)") 13 } 14 15 for description in a.values{ 16 println("description=\(description)") 17 } 18 19 for (code,description) in a{ 20 println("code=\(code),description=\(description)") 21 }
注意:在Swift中集合的可变性不是像Objc一样由单独的数据类型来控制的,而是通过变量和常量来控制,这一点和其他高级语言比较类似。
元组(Tuple)
在开发过程中, 有时会可以临时组织一个数据类型,此时可以使用一个结构体或者类,但是由于这个类型并没有那么复杂,如果定义起来有比较复杂,此时可以考虑使用元组。
1 /** 2 * 元组的基本用法 3 */ 4 var point=(x:50,y:100) //自动推断其类型:(Int,Int) 5 point.x //可以用类似于结构体的方式直接访问元素,结果:50 6 point.y //结果:100 7 point.0 //也可以采用类似数组的方式使用下标访问,结果:50 8 point.1 //结果:100 9 10 //元组也可以不指定元素名称,访问的时候只能使用下标 11 let frame:(Int,Int,Int,Float)=(0,0,100,100.0) 12 println(frame) //结果:(0, 0, 100, 100.0) 13 14 //注意下面的语句是错误的,如果指定了元组的类型则无法指定元素名称 15 //let frame:(Int,Int,Int,Int)=(x:0,y:0,width:100,height:100) 16 17 18 var size=(width:100,25) //仅仅给其中一个元素命名 19 size.width //结果:100 20 size.1 //结果:25 21 22 23 var httpStatus:(Int,String)=(200,"success") //元组的元素类型并不一定相同 24 25 var (status,description)=httpStatus //一次性赋值给多个变量,此时status=200,description="success" 26 27 //接收元组的其中一个值忽略另一个值使用"_"(注意在Swift中很多情况下使用_忽略某个值或变量) 28 var (sta,_)=httpStatus 29 println("sta=\(sta)") //结果:sta=200 30 31 /** 32 * 元组作为函数的参数或返回值,借助元组实现了函数的多个返回值 33 */ 34 func request()->(code:Int,description:String){ 35 return (404,"not found") 36 } 37 var result=request() 38 result.0 //结果:404 39 result.1 //结果:not found 40 result.code //结果:404 41 result.description //结果:not found
可选类型
所谓可选类型就是一个变量或者常量可能有值也可能没有值,则可设置为可选类型。在Objc中如果一个对象类型没有赋值,则默认为nil,同时nil类型也只能作为对象类型的默认值,对于类似于Int等基本类型则对应0这样的默认值。由于Swift是强类型语言,如果在声明变量或常量时没有进行赋值,Swift并不会默认初值。
/** * 可选类型基础 */ var x:Float? //使用?声明成一个可选类型,如果不赋值默认为nil x=172.0 var y:Float=60.0 //var z=x+y //注意此句报错,因为Int和Int?根本就是两种不同的类型,在Swift中两种不同的类型不能运算(因为不会自动进行类型转化) var z=x!+y //使用!进行强制解包 var age="29" var ageInt=age.toInt() //注意ageInt是Int可选类型而不是Int类型(因为String的toInt()方法并不能保证其一定能转化为Int类型)
1、Swift中类似Int和Int?并不是同一种类型,不能进行相关运算,如果要运算只能解包。
2、可选类型其本质就是此类型内部存储分为“Some”和“None”两个部分,如果有值则存储到“Some”中,没有值则存储到“None”,使用感叹号强制解包的过程就是取出“Some”部分,既然可选类型可能有值,也可能没有值,这个时候就需要判断,可以使用if直接判断一个可选类型是否为nil,这样一来就可以根据情况进行强制解包,另外一个选择:判断的时候如果有值则将值赋值给另外一个临时变量或常量,这个过程称为“可选绑定”。
1 /** 2 * 可选类型判断 3 */ 4 var age="29" 5 var ageInt=age.toInt() //注意ageInt是Int可选类型而不是Int类型(因为String的toInt()方法并不能保证其一定能转化为Int类型) 6 7 if ageInt==nil { 8 println("ageInt=nil") 9 }else{ 10 println("ageInt=\(ageInt!)") //注意这里使用感叹号!强制解析 11 } 12 13 /** 14 * 可选类型绑定 15 * 如果可选类型有值则将值赋值给一个临时变量或者常量(此时此变量或者常量接受的值已经不是可选类型),如果没有值则不执行此条件 16 */ 17 if let newAge=ageInt{ //此时newAge可以定义成常量也可以定义成变量 18 println("newAge=\(newAge)") //注意这里并不需要对newAge强制解包 19 }else{ 20 println("ageInt=nil") 21 }
通过前面的演示可以看出Swift中的可选绑定如果实际计算不得不进行强制解包,如果一个可选类型从第一次赋值之后就能保证有值那么使用时就不必进行强制解包了,这样情况下可以使用隐式可选解析类型,通过感叹号声明。
/** * 隐式解析可选类型 */ var age:Int!=0 //通过感叹号声明隐式解析可选类型,此后使用时虽然是可选类型但是不用强制解包 age=29 var newAge:Int=age //不用强制解包直接赋值给Int类型(程序会自动解包) if var tempAge=age { println("tempAge=\(tempAge)") }else{ println("age=nil") }
运算符:
Swift中支持绝大多数C语言的运算符并改进以减少不必要的错误(例如等号赋值后不返回值),算术运算会检查溢出情况,必要时还能使用新增的溢出运算符。另外Swift中还可以对浮点数使用取余运算符,新增了区间运算符。对于基本的运算符这里不再一一介绍,简单看一下Swift中的区间运算符和溢出运算符。
1 /** 2 * 区间运算符,通常用于整形或者字符范围(例如"a"..."z") 3 */ 4 for i in 1...5 { //闭区间运算符...(从1到5,包含5) 5 println("i=\(i)") 6 } 7 8 for i in 1..<5{ //半开区间运算符..<(从1到4) 9 println("i=\(i)") 10 } 11 12 var str = "hello world." 13 var range="a"..."z" 14 for t in str { 15 if range.contains(String(t)) { 16 print(t) //结果:helloworld 17 } 18 } 19 20 /** 21 * 溢出运算符 22 */ 23 var a=UInt8.max //a=255 24 //var b:UInt8=a+1 //注意b会出现溢出,此句报错 25 26 //下面使用溢出运算符,结果为:0,类似的还有&-、&*、&/ 27 //使用溢出运算符可以在最大值和最小值之前循环而不会报错 28 var b:UInt8=a &+ 1
溢出运算符的原理其实很简单,例如对于UInt8,如果8位均为1则十进制表示是255,但是当加1之后则变成了9位“100000000”,出现了溢出但是UInt8本身值只能存储8位,所以取后面8位就变成了“00000000”,十进制表示就是0.
控制流
Swift中的多数控制流和其他语句差别并不大,例如for、while、do while、if、switch等,这里介绍一些不同点。
1 var a=["a","b","c","d","e","f","g"] 2 let b=a[1] 3 /** 4 * switch支持一个case多个模式匹配,同时case后不用写break也会在匹配到种情况后自动跳出匹配,不存在隐式贯穿,如果想要贯穿在case之后添加"fallthrough"关键字 5 */ 6 switch b{ 7 case "a","b": 8 println("b=a or b=b") 9 case "c","d","e","f": 10 println("b in (c,d,e,f)") 11 default: 12 println("b=g") 13 } 14 15 /** 16 * 匹配区间,同时注意switch必须匹配所有情况,否则必须加上default 17 */ 18 19 let c:Int=88 20 switch c{ 21 case 1...60: 22 println("1-60") 23 case 61...90: 24 println("61-90") 25 case 91...100: 26 println("91-100") 27 default: 28 println("1>c>100") 29 } 30 31 /** 32 * 元组匹配、值绑定、where条件匹配 33 * 注意下面的匹配没有default,因为它包含了所有情况 34 */ 35 var d=(x:900,y:0) 36 switch d{ 37 case (0,0): 38 println("d in (0,0)") 39 case (_,0): //忽略x值匹配 40 println("d in y") 41 case (0,let y)://值绑定 42 println("d in x,y=\(y)") 43 case (-100...100,-100...100): //注意这里有可能和第一、二、三个条件重合,但是Swift允许多个case匹配同一个条件,但是只会执行第一个匹配 44 println("x in(0-100),y in (0-100)") 45 case let (x,y) where x==y: //where条件匹配,注意这里的写法等同于:(let x,let y) where x==y 46 println("x=y=\(x)") 47 case let (x, y): 48 println("x=\(x),y=\(y)") 49 50 }
在其他语言中通常可以使用break、continue、return(Switch中添加了fallthrough)等来终止或者跳出某个执行语句,但是它们的功能一般都是固定的,例如break只能终止其所在的内层循环,而return只能跳出它所在的函数。在Swift中这种控制转移功能得到了加强,可以使用标签跳转至任意指定的位置,下面就是使用标签直接跳出最外层循环:
1 var a=5 2 whileLoop: 3 while --a>0 { 4 for var i=0;i<a;++i{ 5 println("a=\(a),i=\(i)") 6 7 break whileLoop 8 //如果此处直接使用break将跳出for循环,而由于这里使用标签直接跳出了while,结果只会打印一次,其结果为:a=4,i=0 9 } 10 }