前言
Swift,苹果于2014年WWDC(苹果开发者大会)发布的新开发语言,可与Objective-C 共同运行于Mac OS和iOS平台,用于搭建基于苹果平台的应用程序。
在2016年的WWDC上,Swift 3.0是随着iOS 10和Xcode 8一起发布的。使用Xcode 8适配Swift 3.0的过程中,你会意识到Swift 3.0有很大的改动,也许你认为从Swift 1.2到2.0已经是很大的改动,我觉得这真的不算什么,因为你还没有见到swift 3.0的全貌,它的改动更多更大。本文是基于Swift 3.0带你初步认识Swift的基础的,仅学习和参考使用。
进入主题
1. 常量和变量
Swift中定义常量和变量分别使用 let / var let:用来定义一个常量 var:用来定义一个变量。常量名和变量名是可以用中文的。
常量是不能修改的,修改是会报错的, 变量却是相反,是可以修改的。在Swift中定义一个常量和变量不需要写明数据类型,因为编译器会根据后面的数据自动推算出对应的类型,如下图我定义分别一个常量和一个变量。通过 option+左键 可以查看对应的类型。 下面的代码截图中,只需要将第二行代码删除即可消除错误。
常量名和变量名使用中文如下截图:
注意点: Swift开发中每一条语句后面不需要写分号,但是写上去也不会报错的,如果同一行有多条语句,那么每一条语句后面必须加上分号,不然编译器不能区分从而报错!
2. 数据类型转换
Swift中不存在隐式类型转换,所有的类型转换必须是显示的,因为Swift是强语言,OC是弱语言,写法如下截图。
3. Bool和Switch
Swift中的 if 使用方式基本和OC一致,但是在Swift中的 if 可以省略 () ,然而在Swift中哪怕 if 后面只有一条语句,也是不能省略 {} 的,但是在OC中是可以的。还有在OC和C或者Java中,一直有个真理:非零即真,但是在Swift中,条件只能放bool类型,所以只能是 true 或者 false。
Swift中的Switch 后面的()可以省略掉
在OC中的Switch如果没有break会穿透,但是在Swift中是不会的
OC中如果要在case中间定义变量,必须加上 {} 确定作用域,而在Swift中是不用的
在OC中default的位置是可以随便写的,只有所有的条件不满足才会执行default,但是在Swift中的default只能放在最后面
OC中的default可以省略,但是在Swift中大部分是不能省略的
4. 三目运算符(三元运算符)和Optional(可选)
三目运算符中的?前后一定要用空格空开,不然会报错
Optional 是一种可选类型,代表的是可有可无,就像OC中的代理方法可以设置为Optional一样
以后凡是看到一个方法和数据类型后面有?的,就是代表的是一个可选类型(Optional),创建出来的对象就有可能为nil
使用可选类型需要注意,如果直接打印可选类型,那么打印出来的值是会被Optional包裹
如果我们使用过程中遇到可选类型,可以通过使用!强制解析,告诉编译器可选类型一定有值(直接在对象后面加!),如果解析后的对象不为空,那么打印出来的就不会包含Optional
但是如果我们强制解析,而可选类型没有值的话,那么程序就会奔溃
5. for循环和While循环
直接上截图 for循环
直接上截图while循环,注意点: do while中的do没有了,因为do被用作捕获异常了,用 repeat 代替
6. 元组
元组(tuples)把多个值组合成一个复合值。元组内的值可以是任意类型,并不要求是相同类型。元组很像C语言中的结构体,但是元组其实就是值传递,而不是引用。元组使用的最佳场景就是当一个函数返回多个不同的类型时,可以返回元组实现。
7. 数组和字典
Swift中的数组和OC中的区别就是:去掉前面的@符号 ,遍历数组就通过元组去遍历
Swift中的字典创建和创建数组一样,都是用[],不过字典是通过 : 来区分一个key对应一个值的,和数组一样,也是通过分号(,)区分下一个元素
[String:NSObject] 类型是IOS开发中最常见的一种格式
8. 字符串
在Swift中使用 "" 定义字符串,在OC中字符串的类型是NSString,而在Swift中却是String
在OC中字符串是一个对象,继承于NSObject,而在Swift中字符串是一个结构体,所以其性能要比OC中的强
9. 函数(方法)
Swift中函数的定义为: func +函数名字(参数名:参数类型,参数名:参数类型) -> 返回类型{}
下面截图分别定义四种不同的函数
定义完对应的函数后,我们要调用,调用方式为: self.函数名 或者 直接函数名
我们可以通过函数名以及对应类型的参数值来调用函数,函数的参数传递的顺序必须与参数列表相同。
上面的都是对应的实例方法,下面就说一下最后的类方法(通过类名去调用)。类方法就是在实例方法的前面加关键字class(在func前面加class),类型方法和实例方法一样用点号(.)语法调用。
10. 闭包(相当于OC中的Block)
闭包和OC中的Block是很相似的,OC中的Block是匿名的函数
在Swift中的闭包也是用来定义一个函数回调用的,所以也是一个函数哈
闭包的基本格式: 闭包名字:(形参列表)->返回类型, (就是函数的写法)
func easyName(finish:(_ intValue:Int)->String,_ string:String){} // 这句代码的意思是定义了一个名字为easyName的函数,函数里面有两个参数,其中一个是闭包类型的finish , 其中一个就是String类型的string,这里面的 (_ intValue:Int)->String 就是一个闭包,闭包传入一个Int类型和返回一个String类型
闭包的展开基本格式: { (形参列表) -> 返回值 in 需要执行的代码 } // in 的含义是用于区分形参返回值和执行代码
// 例子 { (a:Int,b:Int)->Int in return a+b } // 这句代码的意思是: 闭包分别传过来了两个Int类型的a 和 b 并且要返回一个Int类型, in 后面执行的代码就是闭包的返回值(a+b)
1. 函数只有一个参数,并且这个参数是闭包,写法如下:
2. 函数中的多个参数和一个闭包,写法如下:
3. 函数中的多个参数和多个闭包,写法如下:
4. 给闭包定义一个别名
typealias 是Swift中用来为已经存在的类型重新定义名字的关键字(类似于OC语法中的 typedef),重新命名的新名字用来替代之前的类型,并且能够使代码变得更加清晰简单容易理解。typealias 的用法很简单,直接用 = 赋值就可以了
typealias <type name>= <type expression>
下面我这边直接使用闭包别名 AllActionName2 作为函数的参数使用,如下截图
调用函数 easyUseClosure () 如下截图结果
5. 闭包的循环引用问题
我这里先定义一个闭包的属性,通过函数去强引用,然后在回调里面去调用本控制器中的view中的背景颜色,具体操作和解决方法如下:
上面函数前面的private(私有) 关键字 是用来说明这个函数只能在本类中使用,是私有的函数,不能被外部使用。
@escaping 关键字是 所有的闭包都默认为非逃逸闭包,不再需要@noescape;如果是逃逸闭包,就用@escaping表示。 (如果这个闭包是在这个函数结束前内被调用,就是非逃逸的即noescape。如果这个闭包是在函数执行完后才被调用,调用的地方超过了这函数的范围,所以叫逃逸闭包)。
11. 枚举
枚举简单的说也是一种数据类型,只不过是这种数据类型只包含自定义的特定数据,它是一组有共同特性的数据的集合
Swift 中使用 enum 关键词来创建枚举并且把它们的整个定义放在一对大括号内:
enum enumname{
// 枚举定义放在这里
}
定义如下:
使用如下截图:
12. 结构体
Swift 结构体是构建代码所用的一种通用且灵活的构造体,我们可以为结构体定义属性(常量、变量)和添加方法,从而扩展结构体的功能。
与 C 和 Objective C 不同的是:1. 结构体不需要包含实现文件和接口。 2. 结构体允许我们创建一个单一文件,且系统会自动生成面向其它代码的外部接口。
结构体总是通过被复制的方式在代码中传递,因此它的值是不可修改的。
我们通过关键字 struct 来定义结构体, 定义如下:
使用和调用如下:
从实例中我们可以很好的理解到: 结构体实例是通过值传递的而不是通过引用传递。
按照通用的准则,当符合一条或多条以下条件时,请考虑构建结构体:
1. 结构体的主要目的是用来封装少量相关简单数据值
2. 有理由预计一个结构体实例在赋值或传递时,封装的数据将会被拷贝而不是被引用
3. 任何在结构体中储存的值类型属性,也将会被拷贝,而不是被引用
4. 结构体不需要去继承另一个已存在类型的属性或者行为
举例来说,以下情境中适合使用结构体:
1. 几何形状的大小,封装一个width属性和height属性,两者均为Double类型
2. 一定范围内的路径,封装一个start属性和length属性,两者均为Int类型
3. 三维坐标系内一点,封装x,y和z属性,三者均为Double类型
13. 类
类是构建代码所用的一种通用且灵活的构造体,我们可以为类定义属性(常量、变量)和函数(方法)。
通过关键字 class + 类名 +继承的类(可以不用继承)+{}
与其他编程语言所不同的是,Swift 并不要求你为自定义类去创建独立的接口和实现文件。你所要做的是在一个单一文件中定义一个类,系统会自动生成面向其它代码的外部接口。
我这里新建一个TestClass的类
使用类:
类名.init() 然后调用其里面的属性 方法
14. 属性和Setting以及Getting方法
在Swift中,如果在某个类中定义一个属性,那么这个属性是要初始化的,否则就是报错
如果占用时不想初始化,那么可以在后面添加一个?号,可选类型
下面直接通过调用函数,在函数里面赋值和打印查看效果,截图如下:
Swift中的 set 和 get 方法直接在属性的后面添加{ get{} set{} } 具体如下:
调用和打印如下:
在OC中通过关键字 readOnly 修饰 重写 getting方法就是只读属性, 在Swift中 只重写 getting方法,不重写 setting方法就是 只读属性,而且这个属性称之为计算型属性,这个计算型属性是不占内存空间的
属性中还有一个方法可以重写, didSet{} 这个当我设置完属性的值之后会被调用的,具体如下
15. 下标脚本
下标脚本可以定义在类(Class)、结构体(structure)和枚举(enumeration)这些目标中,可以认为是访问对象、集合或序列的快捷方式,不需要再调用实例的特定的赋值和访问方法。
举例来说,用下标脚本访问一个数组(Array)实例中的元素可以这样写 someArray[index] ,访问字典(Dictionary)实例中的元素可以这样写 someDictionary[key]。
对于同一个目标可以定义多个下标脚本,通过索引值类型的不同来进行重载,而且索引值的个数可以是多个。
与定义实例方法类似,定义下标脚本使用subscript关键字,显式声明入参(一个或多个)和返回类型。
与实例方法不同的是下标脚本可以设定为读写或只读。这种方式又有点像计算型属性的getter和setter
这边我在结构体体重定义一个下标脚本:
调用和返回的值是:
在类中使用一个下标脚本:
调用结果如下:
16. 继承
一个类获取了另外一个类的方法和属性,这就是继承。
当一个类继承其它类时,继承类叫子类,被继承类叫超类(或父类)。
在 Swift 中,类可以调用和访问超类的方法,属性和下标脚本,并且可以重写它们。
没有继承其它类的类,称之为基类(Base Class)。
子类可以通过继承来的实例方法,类方法,实例属性,或下标脚本来实现自己的定制功能,我们把这种行为叫重写(overriding),我们可以使用 override 关键字来实现重写。
调用和对应的结果如下:
17. 类型转换
Swift 语言类型转换可以判断实例的类型。也可以用于检测实例类型是否属于其父类或者子类的实例
Swift 中类型转换使用 is 和 as 操作符实现。
is 来检查一个实例是否属于特定子类型。若实例属于那个子类型,类型检查操作符返回 true,否则返回 false。
向下转型,用类型转换操作符(as? 或 as!)。当你不确定向下转型可以成功时,用类型转换的条件形式(as?)。条件形式的类型转换总是返回一个可选值(optional value),并且若下转是不可能的,可选值将是 nil。只有你可以确定向下转型一定会成功时,才使用强制形式(as!)。当你试图向下转型为一个不正确的类型时,强制形式的类型转换会触发一个运行时错误。
类型转换也可以用来检查一个类是否实现了某个协议
Swift为不确定类型提供了两种特殊类型别名: AnyObject可以代表任何class类型的实例。Any可以表示任何类型,包括方法类型(function types)。
我这里新建一个继承于的SonClass的孙子类,SonClass是继承于 baseClass的
调用和使用如下截图:
18. 懒加载
懒加载的格式:定义变量前使用 lazy 关键字来修饰,后面通过赋值一个闭包
注意点 1.必须使用 var 2.闭包后面必须跟上()
如果闭包用于懒加载,那么in之前的代码都可以删掉,包括in在内
19. 构造方法
我这边测试新建一个继承NSObject的类,名字为: TestClass 在类中添加 name hobby和age三个属性,通过重写构造方法和自定义构造方法给三个属性分别赋值,具体操作如下:
类的函数调用和打印如下:
20. 命名空间
在Swift中,如果想使用一个类,是不需要导入(import)对应的头文件的,因为Swift中新增了一个叫做"命名空间"的概念,只要是在同一个命名的空间中,那么资源都是共享的,默认的情况下,项目名称就是命名空间,如下截图,我这边的命名空间就是:Test_Swift 。可以从截图中看出 ViewController是在其命名空间之中的。
. 将字符串转为类(动态获取命名空间)
具体代码如下:
方法的调用如下
21. 扩展(extension),相当于OC中的类别
给对应的类或者系统的类添加方法或者属性。
写法: 写在类的外面,用关键字 extension 修饰,后面跟随着要扩展的对应的类,然后在{}里面写要扩展的方法或者属性
以上面的获取动态空间为例,我给Bundle 扩充一个方法和一个属性,方法就是用来获取命名空间的,属性的话就简单的一个打印操作吧。具体实现如下截图:
代码的调用和打印如下:
22. 协议
协议规定了用来实现某一特定功能所必需的方法和属性。
任意能够满足协议要求的类型被称为遵循(conform)这个协议。
类,结构体或枚举类型都可以遵循协议,并提供具体实现来完成协议定义的方法和功能。
协议的语法格式如下:
protocol 协议名字 { // 协议内容 }
要使类遵循某个协议,需要在类型名称后加上协议名称,中间以冒号:分隔,作为类型定义的一部分。遵循多个协议时,各协议之间用逗号,分隔。
struct 结构体名字:FirstProtocol,AnotherProtocol{ // 结构体内容 }
如果类在遵循协议的同时拥有父类,应该将父类名放在协议名之前,以逗号分隔。
class 类名:父类名,FirstProtocol,AnotherProtocol{ // 类的内容 }
具体定义一个协议和遵守如下截图:
调用如下:
23. 泛型
Swift 的数组和字典类型都是泛型集
你可以创建一个Int数组,也可创建一个String数组,或者甚至于可以是任何其他 Swift 的类型数据数组
最后,原文Demo地址
可以通过下面的截图快速查看和学习