swift概述
swift 可以开发iOS, macOS, watchOS, 和 tvOS apps 。
swift语法学习可以新建一个playground项目
Swift快速预览
swift打印hellow world不需要main函数 不需要import include,只需要一句
简单的值
用let声明一个常量用var来声明一个变量。
如果将浮点型赋值给整型 必须用构造的形式,Int ,如果将整型赋值给浮点型,不需要用构造,直接赋值
另外如果不做任何处理就把nil赋值给常量或变量是不允许的,必须在=前面加上?
字符串声明与拼接
数组必须是指定的特殊类型,字典的key必须是hashable类型的,比如Int、String、Double、Float
声明一个空的数组
数组添加一个元素
数组删除一个元素
修改一个数组的变量的值为空数组
数组遍历
声明一个空的字典
字典添加一个元素
字典删除一个元素
字典遍历
控制流
用if 和switch 做条件判断,用for in ,for ,while ,repeat-while来做作循环
先来看一个没啥变化的
可以通过let a =
的形式来判断一个变量是否为nil,下面两种形式同样效果
两个??
可以用来表示如果值为nil使用默认值
switch在swift中无需使用break,程序会自动跳出,如果不想跳出可以用,
链接使用,另外条件也可以使用let作判断条件,示例如下
while单纯使用没什么变化,多了一个repeat,while的用法,
for in的时候相当于之前oc中的for(int i=0;i<5;i++)。这里需要注意..<
不包含后面的值,...
包含后面的值,注意是三个.
函数和闭包
普通函数的返回值和输入参数写法与oc有所不同
可以不写第一个参数的label,或者重命名label
返回值可以不是一个值,可以是一个组合
函数参数可以是不固定参数
函数可以嵌套函数
函数可以作为返回值来返回
函数可以用另一个函数作为参数
block的使用{ in }
当变量的类型可以确定的情况下可以省略
你可以用数字来替代变量。当block当作仅有的一个参数时()
可以省略
类和对象
类中的property和上面的变量一样声明。常量用let。创建一个对象用类名加上()
,如果init函数有其他参数,可以在类名后面的()
里面加上参数。访问一个类的property和方法都用.
willSet
和 didSet
可以在property的值发生改变之前或之后调用,除了初始化值以外。
当需要的东西可以是nil时,你可以在操作方法属性脚标之前写?
,当?
之前的东西是nil时,后面的操作就忽略了,如果你不加?
,但是你的值是nil,则会crash掉哦。
枚举和Structures
用enum
来创建一个枚举。与oc不同的是,枚举也可以有方法。
利用init?(rawValue:)
的形式创建一个枚举对象,例如
struct和class用法一样,唯一不同的是struct类型的对象赋值给另外一个变量的时候是拷贝,而class类型的对象则是引用计数加一
协议和扩展
利用protocol来声明一个协议
与oc不同的是struct和class 枚举都可以继承协议,只有class在修改实例变量的时候不需要mutating,其他均需要,所以一般如果协议中要定义某个方法时,如果这个协议不只针对class,最好加上mutating
extension 和oc的category用法一样,用来扩展已有的类,加上一些方法
错误捕捉
如果你要自定义一些错误枚举类型,需要继承自Error协议
用throw
可以抛出异常,用throws
来标记一个函数可以抛出异常
你可以使用do-catch来捕获异常,在do的代码块中,你把try放在可以抛异常的函数前面。在catch的代码块中,如果你不指定一个特别的名字,那么自动为error。
当然你也可以写不同的代码catch代码块来捕捉不同的错误。例如
范型
比如有个这样的需求,写一段代码可以构造出任意类型的数组,用oc可以很容易,直接用NSArray就可以,但是在swift中,类型一经指定不可改变。这就需要范型
范型可以应用在class、struct、枚举、方法、函数中
在代码块之前写where可以加一些过滤条件例如需要这种类型必须继承自某协议或者两种类型相同、或者需要这种类型有个特定的父类
语言指南
基础
swift中Int对应整型,Double和Float是浮点类型,Bool是布尔类型,String文本类型。可用容器有Array、Set、Dictionary。
相对于oc,swift加入了一个新的类型 tuples.即函数可以返回多个值。
swift也支持有缺省值的类型,也就是说一个变量可以有值x或者酒没有值。这就像oc中使用nil一样,不过在swift中不仅仅对class对于其他类型枚举struct也同样适用。
打印
oc中用nslog,swift中用print
注释
单行注释用// 多行使用/**/
分号
一般可以不写如果在同一行中写多句代码则需要分号;
分开
整型
swift中可以使用8位16位和32位的格式。如UInt8、UInt16、UInt32、如果想获取最小值最大值可以这样let minValue = UInt8.min
swift中如果是32位手机里Int是Int32,UInt是UInt32。在64位手机里Int是Int64位,UInt是UInt64
swift中10进制不加任何前缀,二进制加0b,八进制加0o,16进制加0x
如果给一个已知的类型重命名可以用typealias
例如typealias AudioSample = UInt16
布尔类型
swift中布尔类型的值是true和false
容器
swift中提供三种类型的容器分别是array、set、dictionary
swift中非常清楚容器中对象的类型,也就是说你不能往容器里存放不允许的类型的数据。
如果你创建了一个array或者set或者dictionary,并且赋值给一个变量,则都是可以编辑的,往里面添加对象删除对象,但是如果赋值给常量,则不可编辑。
array
创建一个数组并给初始值
两个数组合并
用一个数组来初始化一个数组
用isEmpty
来判断是否是空。添加一个元素可以用append
或者+=
或者insert at
set
当你需要容器里的对象有序或者你希望容器里的对象是唯一的可以考虑用set。
set容器里的对象类型必须是hash类型的,即if a == b与 a.hashValue == b.hashValue.等价。在swift中基础类型,String、Int、Double、Bool都默认是可以hash的,可以存储在set中或者作为dictionary的key值。枚举默认也是可以hash的。
set的两个对象之间四种操作结果
Dictionaries
控制流
if、 while、repeat while、switch等 与oc不同的是
switch中做条件的可以是字符串类型也可以是Tuples(组合),oc中必须是数字
swift除了支持continue break 之外还支持fallthrough向下执行一个case。
guard else
和if(!)一样,不同的是必须guard和else一起使用
检查api是否可用
类和结构体
swift中类和结构体的共同点:
1、可以定义属性properties
2、可以定义方法
3、可以用脚标(subscript)的方式访问
4、可以定义初始化方法
5、可以给现有的类 进行扩展
6、可以遵守某个协议
不同点:
1、类可以继承
2、子类和父类转化及运行时判断类的类型
3、类可以定义析构函数
4、类支持引用计数
类是引用类型
与值类型的不同的是,引用类型在赋值给常量或者变量或者函数传参数的时候的时候并没有拷贝。
唯一符号
swift中用===
来判断是是同一个实例,用!==
来判断不是同一个实例
使用建议
如果你的对象中的成员都是基本类型的数据,或者对象不需要从其他类继承属性或者方法 都应该使用结构体
在swift中String、Array、Dictionary都是用结构体来实现的,这就意味着在赋值给常量或者变量,或者函数传参数的时候都是拷贝。这和oc中的NSString、NSArray、NSDictionaray是不同的,oc是以类实现的,在赋值的时候都是引用。
属性(Properties)
swift中的let 和var声明的常量和变量就是 oc中的属性。swift支持关键字lazy,用来支持不用的时候不初始化,用的时候才初始化,更节省内存。例如
swift支持在属性将要改变或者已经改变的时候做一些事情
类变量
有两个关键字可以实现类变量,static和class。那么什么时候该用什么呢?struct和enum用static。类中一般也用static,但是如果你想让子类重载该变量的get方法时必须用class。
在Swift中class、struct和enum都是可以实现protocol的。那么如果我们想在protocol里定义一个类型域上的方法或者计算属性的话,应该用哪个关键字呢?答案是使用class进行定义
Methods 方法
结构体和枚举是值类型的,在默认情况下,如果属性属于值类型的变量则是不允许修改的,如果你非要修改的话,必须加上mutating
Subscripts 脚标
类、结构体、枚举都支持利用脚标来访问比如
继承
只有类可以继承,如果想重载父类的方法可以用override
关键字
init
一般默认就是没有参数的init,和自定义的和省略参数名称的。
如果在类中没有写自定义的初始化函数,则用默认的初始化函数可以将所有属性初始化默认值
如果在类中没有写自定义的初始化函数,还可以用属性初始化函数来进行初始化
推荐初始化函数(Designated) 和 快捷初始化函数(Convenience)
在oc中,如果是推荐初始化函数可以使用NS_DESIGNATED_INITIALIZER
来修饰,比如NSArray
这里面这三个都是推荐使用的初始化函数,你用哪个都可以。也就是说推荐函数可能不是一个。我总结了以下几个原则:
1、在创建一个实例的时候尽量使用推荐的初始化函数
2、在快捷初始化函数中一定要调用某个推荐的初始化函数,以保证对象的所有的属性都进行了初始化
3、如果是重载的父类的推荐初始化函数,如果在子类也就是本类中也是推荐初始化函数,则需要调用父类的本初始化函数,如果在本类中是快捷的初始化函数,也就是不带NS_DESIGNATED_INITIALIZER
关键字,则需要调用本类中的一个推荐初始化函数。
4、如果不想让其他人使用快捷初始化函数初始化对象,比如AFNetworking中的AFNetworkReachabilityManager,不想让使用者通过init
初始化则在后面加上关键字NS_UNAVAILABLE
, 并且返回nil
在swift中与oc相反,推荐初始化函数不需要任何写关键字,而快捷初始化函数则要在前面加上关键字convenience
如果是重载的父类的初始化函数则需要在前面加关键字override
如果想让子类必须重载某个函数则需要在前面加上required
关键字,而子类在重载的时候同样需要写关键字required
,而不是override
Deinitialization 反初始化函数
顾名思义,就是在对象释放的时候调用deinit
函数。在deinit
函数里你可能需要做一些清理的工作,比如你在某个函数里打开了某个文件,在这里一定要关闭。
自动引用计数ARC
swift使用了ARC来管理内存。在swift中使用weak
或者unowned
来处理两个类之间的循环引用的问题.如果要解决闭包中循环使用的问题,则需要在使用前先前置使用列表,列表中用weak
或unowned
来修饰。
自判断链接(Optional Chaining )
swift中允许某个对象的值为nil,在执行的时候如果值为nil则后面的所有操作则失效,不会闪退。前提是写的时候这样写var residence: Residence?
错误捕捉
类型转换
在swift中,如果判断一个对象是否是某种类型可以用is
,如果将父类的对象转换成子类的对象可以用as!
或者as?
,如果你觉得转换会失败则用as?
,如果你确认必定会转换成功则用as!
。
另外swift中提供两个特殊的关键字Any
和 AnyObject
,其中Any
可以代表所有的swift中的类型,AnyObject
可以代表任何类型的对象。
扩展(Extensions)
swift中的Extensions有点和oc中的categories相似,只不过Extensions没有名字。其作用是对现有的类进行扩展,详细有:
1、添加属性和计算属性
2、定义对象的方法
3、提供一些新的初始化函数
4、定义脚标
5、定义和使用嵌套类型
6、让现有的类遵守某协议
协议(protocol)
简介
在swift中类、结构体、和枚举都可以实现协议。
类型的方法或者计算属性
与oc不同的还有,协议里可以声明类的方法或者结构体枚举的方法,而不是对象的方法,其中类用class
,其他的用static
。
还可以在协议里写计算属性哦,记得不是存储属性。
初始化函数
协议里也可以写初始化函数,如果类遵守了这种协议需要在初始化函数前面加关键字required
作为参数适用
另外协议还可以作为类型来使用:
1、作为函数或者方法或者初始化函数的参数或者返回值
2、作为变量、常量、属性的的类型
3、容器里的数组、字典、或者其他容器的里的成员的类型
扩展中使用协议
如果类中已经实现了协议里的方法,但是没有遵守协议,可以这样写
协议类型的容器
继承
在swift中协议可以继承,一个一协议可以继承多个协议
只有类才可以使用的协议
你可以在协议后面的继承列表中第一个位置写关键字class
,来表明这个协议只给类使用,结构体和枚举是不允许使用的
协议中的不必实现的方法
在oc中如果某些方法不是必须实现的,可以在前面加上关键字optional
,在swift中如果要实现这种功能必须要在协议和不必实现的方法前面加上@objc
协议的扩展
在swift中协议也是可以扩展的
泛型
swift中泛型是为了对象解决类型不一样,但是处理的代码都一样的问题,如果这些对象属于不同的类,而这些类不是继承自同一个基类,相互之间没有任何关系,那么可以用泛型来解决问题。比如一个交换函数,可以交换字符串、整型等等。使用起来先用<T>
声明,然后用T
来代表类型。
作用域
在开始讲swift的作用域之前我们要先了解一个关键词module
,也就是模块。这跟git中的submodule有点类似,在iOS中一个可以发布的模块,比如一个framework或者一个程序,总之他们可以被其他的模块用import
来访问。另外一个关键字是entity,是指属性、变量、常量、方法、函数等,都可以成为entity,实体。
swift中的作用域有五种:
1、open
和public
可以定义实体在本模块中使用,可以在其他模块通过import使用。至于这两者区别在于open
可以在其他module中被继承和重载,而public
则不可以。
2、internal
只能在本模块中使用。
3、fileprivate
只能在本文件中使用
4、private
作用于某个类,比如class A中如果变量a是private那么除了A类以外的类都不能访问了。另外如果在类中变量定义成了private类型的,它的扩展中是访问不了的,需要定义为fileprivate。
默认作用域
默认是internal
即只能在本模块中使用,也有特别的,如元组(Tuple),如果元组返回两个元素一个为private一个internal,则最终这个元组的访问级别是private。
其他注意事项
1、子类的访问级别不能高于父类,如果父类类的访问级别是internal,则子类类的作用域不能是public,至于成员变量、方法、初始化函数、也就是实体,可以通过重载的方式改变为高的。
2、如果你定一个public访问级别的协议,那么这个协议里的所有实体默认是public,这和其他的类型不同,其他的类型例如class是public级别的,但是它的实体却默认是internal