原文:http://www.cnblogs.com/hkJser/p/4618708.html
一、基础语法
1.基础
1.1) swift还是使用// 和/* */ 来注释,并且/* */允许多行注释.
1.2) swift使用print和println打印,它的传参是一个泛型,几乎所有类型都是可打印的.
1.3) swift在语句后面加分号与否都是正确的,但是swift偏好的风格是不在结尾处加分号的.如果有多个语句,必须用分号隔开.
1.4) 在一个数字中庸下划线(_)会被忽视,方便认出大数值的的数字,也可以在前面补零.
1.5) swift不允许在不同种类型间做加减乘除,要先做类型转换或运算符重载.
1.6) typealias 可以为一个类型指定另外一个名字,这样可以清晰的表达类型的意义.typealias newType = OldTypeName
1.7) assert 断言表示自己抛出异常,assert 第一个参数表示 通过而不抛异常的条件,第二个参数表示提示,第二个参数可以没有
1.8) swift中的 = 赋值运算符没有返回值,所以不会犯 if a = 3 {}这样的错误了,运算符前后加空格,以免xcode不能识别
2.变量
2.1) let 定义常量,var 定义变量,let定义的必须在声明时指定初始值,普通的var声明也必须赋初始值.
2.2) let 声明的变量不可再改变,而var变量可以再改变,但是你不能再声明一个已经声明的常量或变量.
2.3) 可以在一行同时声明多个常量或变量,用逗号隔开;如果每个常量对应一个var或let只需要用分号隔开即可.
2.4) 每个常量和变量都一定会有固定的类型,如果没有指定类型,swift会根据后面的赋值类型来推断.
2.5) swift的命名不能使用保留字和箭头/开头不能用数字,没有其它任何规定,甚至都可以使用小狗小猫命名.
2.6) 如果你想使用关键字来命名是不允许的,但是你可以在前后加上``来命名,比如`let`,非关键字加``命名也可以的.
2.7) swift里可以声明元组,类型类似于(Int,String),你可以用var声明也可以用let声明,里面两个都会是变量或者常量
2.8) 而元组的方式和直接分开声明两个变量和常量没多大区别,你可以把其中的变量拿出来单独用或当元组用,混合用也可以.
2.9) 可以用元组名直接通过下标索引直接来访问元组里对应的值,比如aaa.0 aaa.1
2.10) 你可以在元组中跟元素命名,比如(code:404,message:"Not Found"),然后你就可以用aaa.code 和 aaa.message来访问
3.类型
3.1) int型的UInt8和Int32 都可以通过min和max来取最大值和最小值
3.2) Double是64位浮点数,Float是32位浮点数,对小数自动推断是Double类型,除非是指定过类型的
3.3) 可以跟指定为浮点类型的常量或变量赋一个整数值但是不能是整数变量,它会自动转换成浮点数.
3.4) 显示指定类型的变量或常量不能跟它赋值一个不同类型的值,它不能做自动转换,除了跟浮点类型赋值一个整数数值
3.5) 在指定类型后加一个?表示可选类型,它的意思是它可能为没有值为nil,然后你可以通过可选绑定或者判断是否为空来使用它.
3.6) 如果是一个class的可选类型,你可以通过?.访问它的属性和方法,它是通过前者是否会相应后面的方法,如果能响应则执行,不能则返回nil
3.7) ?其实是个语法糖,比如String?类型等价于Optional<String>类型,只是方便写而已,他和String类型本质是不同的
3.8) 如果你不想用判断是否为空或者用可选绑定或者用.?来访问它,你可以用最直接最简单的!强制解包来使用,但前提是能保证非空
3.9) 如果一个可选量没有值又强制解包会报错,一个可选类型会隐式赋值为nil,你也可以在使用中赋值为nil
3.10) 之所以需要有可选类型是因为swift是类型绝对安全的语言,它需要你在使用变量时一定有值,但是这种可选类型太麻烦,所以产生了!类型,!类型本质应该就是可选类型
3.11) 声明为!可选类型,也可以不做初始化,它相当于你在每次使用可选类型的时候加上了!进行强制解包,你就不需要再自己加!了,你需要自己保证它不为空,否则使用会出错
4.字符串
4.1) ... 表示闭区间/ ..< 表示开区间,== 表示值相等/=== 表示引用相同,+ 可以直接加字符串或数组
4.2) swift中的string是值类型,它在做常量变量赋值或函数中进行传递时都会做值拷贝,都是一个新的副本,swift只有在必要的情况下做拷贝
4.3) 可以用for in 遍历字符串,用 count全局函数计算字符数,用isEmpty属性判断是否是空字符串,有hasPrefix判断前缀(后缀等)
4.4) 可以用startIndex和endIndex获取开始和结束下标,用数组和下标的形式访问单个字符;还可以通过\()的方式生成字符串
5.数组
5.1) swift中的数组可以用[String]和Array<String>来声明,它们是同一个意思,赋值也直接用中括号[]表示就可以了,里面的值需要是同一个类型
5.2) array 有count/isEmpty等属性,有append/insert/remove等方法,它可以直接 + 一个数组,它可以通过下标和下标区间来取值,区间取到的是一个数组
5.3) for item in shoppingList / for (index,value) in enumerate(shoppingList) 两种遍历方式,后面一种会知道下标
5.4) 可以通过[Double](count: 3,repeatedValue: 3.3) / Array(count: 3,repeatedValue: 2.5) 初始化一个数组
5.5) 数组也是值传递,如果把数组a赋值给数组b,a和b就是两份拷贝,修改相互不会有影响
6.字典
6.1) 字典是通过 [String : String] / Dictionary<String,String> 声明的,形式也是用[a:b,c:d]这种形式,通过键访问值
6.2) 可以直接通过a[b] = c 这种方式增加一个键值对,通过updateValue和removeValueForKey来增加删除键值对
6.3) 可以用for (airportCode,airportName) in airports / for airportCode in airports.keys 遍历键值对或键或值
6.4) 可以直接赋值 [:] 来清空字典,键值是可以为Int的;和string,array一样,dictionary也是值拷贝
7.Set
7.1) 用Set<Int> 声明Set,和数组一模一样跟它赋值,也可以通过赋值[]清除,也必须是同一类型,也有insert/contains方法,isEmpty属性
7.2) Set也有for in 遍历,有取两个集合的差交等做做排列的方法,也有判断子集超集等方法
8.For循环
8.1) for index in 1...5 的这个index只是在当前这个循环的作用域内,如果for循环外面也有一个index,随便修改也不会和它又什么影响
8.2) for _ in 1...10 如果不需要循环的这个索引可以用下划线_代替,在swift中只要不需要那个值的一般都可以用_代替
8.3) for var index = 0; index < 3; ++index 这种for循环也还是可以的
9.Switch语句
9.1) swift 中的switch必须穷举所有的可能,否则必须加default,穷举的时候在一个case后面可以放很多可能值,用逗号隔开就好
9.2) 穷举可以是用...和..<表示的一个区间,对于元组可以一个用_通配符,另一个匹配,或者两个都匹配,或者两个都是区间(只有一个元素也可以做值绑定)
9.3) 做统配的时候可以用var x/let x,或 let (x,y) 进行值绑定,其中var声明的可以修改
9.4) 当做let 做值绑定的时候可以在后面加上 where做条件判断.switch 不需要用break防止穿透,但是它还是会去下一个case判断条件去匹配,使用break还是可以直接跳出switch
9.5) 在switch同样可以使用break/continue跳出一个标签
二、高级语法
1.函数
1.1 func funcNmae()->(){} 这样就定义了一个函数,它的参数为空,返回值为空,如果有参数和返回值直接写在两个括号里就可以了
1.2 参数需要指明类型,而如果没有返回值可以不写->(),返回值只需要写返回类型,如果写了返回名称,可以在函数调用后的值用点语法访问
1.3 在参数名称前可以加上外部参数名,调用的时候就可以带上它了,如果外部参数名和内部参数名相同则可以直接在内部参数名前加#即可
1.4 如果跟参数设置一个默认值,则swift会自动加上外部参数名,如果不想有在前面用下划线就可以_,如果默认值参数不在最后则不能省略,在传参时传一个下划线进去就可以
1.5 在最后一个参数后面加...代表是这个参数是可变参数并且类型就是它,参数个数至少是0个,在函数内可以使用for in 这个参数获得 这些参数
1.6 每个参数前面其实有一个隐藏的关键字let,如果想让参数可变(可赋值)则需要加上var关键字,不过也只是能赋值,因为是值拷贝所以不能修改外部参数的实际值,如果要变成地址传递,需要在参数前加inout 关键字,而实参需要加上&,
1.7 swift中函数其实也只是中类型,函数名就是变量名,比如let func1 : () -> () 声明的就是个无参数无返回值的函数类型,所以如果一个函数返回一个函数的话和返回一个普通变量没什么区别
2.闭包
2.1 闭包代表了一段程序代码,{ (传入参数) -> 返回值的类型 in ... 表达式 ... },函数只是闭包的一个特例
2.2 闭包可以推断返回类型,所以可以省略->返回值类型,参数类型也可以推到,所以参数类型也不要,括号也可以去掉,如果闭包只有一个表达式直接可以省略掉return关键字,因为我们可以用$0/$1简写参数,所以参数也可以省略掉.
2.3如果闭包是函数最后一个参数,则可以去掉小括号,直接用大括号里的内容,不过需要大括号紧接着写,叫尾随闭包
2.4 内层返回函数会捕获外层的变量的值,当内层函数返回后,外层函数的内部变量并不会释放内存,它的变量的值会跟着内部函数的执行而变化
3.枚举
3.1 用enum CompassPoint{ case North,South,East,West}定义枚举,可以不用逗号,和声明语句一样分开用多个case写就可以了
3.2 可以用元组来设定枚举对应每项中的值,并可以用switch case 中的值绑定来判断,128)"> 3.3 枚举类型如果初始化为Int类型,它的下一项也会有一个加1的原始值,不过枚举直接复制成string都可以的
4.结构体
4.1 结构题的属性必须初始化,必须有默认值或者通过构造器init
4.2 结构体本身是值传递,如果一个结构体赋值给另外一个结构体了也是两份拷贝,互相修改不会有影响
4.3 如果一个结构体用let声明了,那么它内部的值就不能再做修改了,var声明的结构体才能修改
4.4 但是class不同,对象的赋值会是同一份引用,修改会影响到另外一个对象,但是let 声明的变量也是不能赋值的,只是能修改它内部的值而已
5.属性(成员变量)
5.1 结构体/类在初始化的时候成员变量一定要有值,如果你没有给出初始化方法,则默认有一个包含所有必须初始化的的init方法,如果你提供了,默认了就没有了
5.2 (延迟属性)用let声明的成员变量,就不能再修改了,如果是一个耗时的属性比如值是一个自定义对象,可以加上lazy属性,它只有在用到的时候才会对这个属性做初始化,避免不要的消耗(延迟属性)
5.3 (计算属性)有的属性是根据其它的属性计算出来的,并不是一个必须的属性,只是让使用的时候方便了些,在属性定义后面加上set/get方法,get方法需要返回一个值,set方法有一个参数,用来设置其它属性,如果不要参数就不要写参数外面的括号,它就有一个默认的参数newValue
5.4 如果只有get方法就是只读属性,只读属性swift提供了一种简写方式,直接把返回语句写在最外面的大括号里就可以了
5.5 swift提供了属性监听方法:willSet和didSet,两个都是一样有一个参数,分别将要设置的值,和属性过去的值,同样你如果不提供参数,会用两个默认的newValue和oldValue.这两个方法和set/get方法并列,在didSet方法中可以直接修改(调整)属性的值,但是这两个方法不能和set/get方法共存
5.6 swift中有类别属性,enum/struct 用关键字static,class就用class关键字,在class中let 声明的需要直接赋初始值,var声明的必须用get方法return,因为swift不允许class储存类属性,而enum/struct可以
6.函数(成员方法)
6.1 class中的函数都不需要加上外部参数,因为除了第一个参数都默认加上了#符号,不过这只是swift帮你做了点事情而已,它并没有在语法上强制你做什么,你想为第一个参数加上外部参数也行,都用_代替默认的外部参数都可以
6.2 在swift里self.x不代表会调用setX/getX方法,所以直接可以在set/get方法里这么用.
6.3 在struct和enum中,成员方法不允许修改成员变量,如果要修改需要加上mutating关键字,但是如果声明的结构体变量是一个let常量的话,这个方法也是不允许调用的.
6.4 在struct和enum中你可以在mutating方法中直接跟self赋值成另外一个变量
6.5 在struct和enmu中用static标识一个方法是类方法,而class中用class关键字
7.角标(subscript)
7.1 重写subscript,类似于subscript(index: Int) -> Int{},里面写set/get方法,和声明变量,根据传参和返回值来确定下标的类型和返回值,重写了这个方法这个对应的类型就可以用角标了.
7.2 subscript方法参数的个数对应角标的个数,例如两个参数:mar[2,3]
8.继承
8.1 swift中没有基础类,所有不继承其它类的的类都是基础类,重写父类中init方法,要先调用super的init方法,然后再在后面修改属性的值,访问属性直接用属性名字就可以了,不用用self等.
8.2 要重写属性和重写方法类似,直接加个override就好了,在重写的set/get方法也可以调用super对应的属性值,或设置值都可以.
8.3 覆盖了didSet属性监视器就不能再覆盖set/get方法了,跟方法或属性加上final关键字可以防止被子类覆盖
9.初始化(init)
9.1 init方法和普通方法一样,你需要在init方法中把每个必须要赋值的属性都赋值,否则会出编译错误,init方法会给每个参数加上#,不要它的话可以用_,在方法内部用self访问,也可以不用
9.2 如果你自定义了init方法,那么swift会不再提供默认的init方法,你可以自己写一个init方法,init方法带不带参数什么都是可以的,自己决定就行
9.3 如果你想一个init方法中调用另一个init方法,需要加上一个convenience关键字,在这个init方法里就可以调用另一个init方法了
9.4 在子类继承父类的时候,首先需要初始化子类的成员变量,然后才能调用super的init方法初始化父类的属性,最后可以修改子类和父类的属性,如果这个属性是父类的,在子类中也是用self访问,因为这个属性已经是它自己的了
9.5 如果子类一个init方法都没有提供,那么子类继承父类所有的构造器,可以用父类的init方法初始化
9.6 在初始化属性的时候,可以用闭包实现,只要在复制的=后面加上{},你们写return和其它语句,最后在{}后面加一个()表示闭包立刻执行,闭包和属性的set方法是类似的,只是在最开始提供的
10.销毁方法(deinit)
10.1 deinit方法会在对象销毁的时候调用,可以打印判断它时候销毁
11.内存管理(ARC)
11.1 可选类型或者普通类型属性只要对一个对象有一个引用,它的这个对象的引用计数就要加1,如果两个对象相互引用就会产生引用循环,所以需要跟其中的一个属性用关键字weak声明为弱引用,就是可以设置为nil
11.2 一般用weak声明的用可选类型,因为它引用的可能为nil,如果你在调用的时候能确定它是有值的,你可以声明为unowned的普通类型,它的效果是如果你能保证调用这个属性时不为nil时,swift建议用unowned,其它都和weak一样的
12.可选链(Optional Chaining)
12.1 对于一个可选类型的属性可以用?.和!.来访问,如果确定有值可以用a!.b!.c!.d,如果不确定有值可以用a?.b?.c?.d
13.类型转化
13.1 可以用 a is b 来判断对象a是否是b类型,返回值是一个boolean类型的值
13.2 as 可以把普通类型转化,比如double,int,cgfloat之类都可以用它转化
13.3 可以用 as? 把之类转化成父类,它的结果可以是nil或者转型成功,所以结果是一个可选类型,转化成功后用?.来访问方法或属性,也可以做可选绑定.
13.4 如果一个对象一定可以转化成功,则可以用as!转化,如果转化不成功会报运行时错误,比如在一个数组中全是animal,但是声明时是anyobject就可以这样用.
14.扩展/协议(categories,extension,protocol)
14.1 swift的extension没有名称,它是扩展到所有的实体类的,它不可以增加成员属性,但是可以增加计算属性
14.2 可以在protocol中声明属性和方法,可以定义属性时候是只读的或者是可读可写的
14.3 可以让一个extension继承一个protocol,在里面实现对应的方法
14.4 协议的类型是protocol<protcl1,protcl2>
15.泛型
15.1可以在func或struct后面跟上<T1,T2>,在参数中就可以声明参数类型为这个类型,完全可以把它看做是一个类型
16.运算符重载
16.1 在swift中运算符可以重载,方法名就是运算符号,参数的个数和类型是根据它是几目运算符和运算符两边对应的参数类型决定的.
1. 可选类型是一种类型,String?就是Optional<String>,所以函数参数也可以声明为它
2. a??b??c 如果a有值返回a,a为nil返回b,b也也为nil,前面的整体结果就是nil返回c.条件是??前面的都必须是optional的,??后面的类型必须和??前面的一致,结果等价于三目运算符a!=nil?a!:b
3. 遵从GeneratorType协议的对象有一个next方法可以用来遍历,你可以自定义generator遵循generatorType协议,自己就可以根据这个方法做循环,你在next方法中写好遍历的顺序就好.
4. 遵从SequenceType协议的对象可以用for in来循环,每个SequenceType对象里面包含对应着一个generator,也可以获取generator后做while循环,也可以直接写for in循环,for in出来每项的值就是generator中next方法的返回值
5. 区间运算符...和..<其实是Range<T>类型的对象,这个对象有一个generate方法,返回的是RangeGenerator<T>类型结构体,它遵循GeneratorType协议,所以...和..< 也能做for in 循环
6. 数组过滤方法可以用闭包,如:oldArray.filter{ $0 > 30 },数组还有个根据数组值计算的的方法,oldArray.reduce(1,combine: {$0 + $1}) 结果就是1和数组中的每个元素相加
7. Int和string都有map方法,someName.map{name in "Hello,\(name)"}??"Hello world!" name是个可选类型,你不需要做可选绑定,如fil果name是nil,这个map方法返回的值也是nil
8. 函数的柯里化就是多参函数变成一个参数调用的返回值是函数,再调用的这个函数传入第二个参数,这中间得到的函数叫外部这个函数的偏函数,函数柯里化只需要把函数的多个参数分别写在不同的括号里就可以了
9. 在类命名访问成员方法,得到的是一个可以函数柯里化的函数,第一个参数是类对应的对象,后面的参数就是这个函数应该有的参数
10. swift的枚举可以有相关值(就是传参),有相关值的两个枚举值不能用==比较,需要自己重载==运算符,而普通的枚举是可以通过==比较的
11. 枚举是可以有原始值的(默认值),可以是String,Character,Int,Float类型的,如果是Int后面的枚举会递增,通过rowValue可以获取到原始值
12. 由于swift初始化方法的安全检查太严格,在设置类似于数据源数组的时候可以用lazy 声明,并用闭包返回值
13. Any表示任何类型除了方法类型,AnyObject表示任何class类型的实例
14. 在协议前加@objc表示协议可选,它只对class有效;在方法和属性前加optional表示属性和方法可选protocol<SomeProtocol,AnotherProtocol>就可以表示遵循这两个协议的对象