参考资源
《swifter》
https://github.com/iOS-Swift-Developers/Swiftgit
闭包逃逸
swift3中,闭包默认是非逃逸的。若是一个函数参数可能致使引用循环,那么它须要被显示的标记出来。
没有逃逸的闭包的做用域是不会超过函数自己的,
weakSelf unowned
若是可以肯定访问时不会被释放的话,尽可能用unowned,若是存在被释放的可能性的话,就用weak。
array 的高级函数 filter map程序员
闭包捕获值
闭包和函数是引用类型
常量和变量
若是定义的时候初始化一个变量,能够不用写数据类型,会根据初始化的值自动推断出变量的类型(其余语言是没有类型推断的)
@objc 是为了 swift 和 oc 之间的交互,在使用 optionalgithub
泛型函数
若是函数的泛型列表只有一个 T ,每一个节点类型的参数必须是同样的。
能够对节点进行一些限制,好比要求泛类型遵照某些协议。
有时候节点中的泛型须要更多的限制,须要使用 where 语句来补充约束条件。
泛型协议
关键字
associaetdType
self :适用于比较这类方法,其必须传入一个相同类型的参数才有意义。正则表达式
方法
实例方法和函数的区别:函数默认没有外部参数
在类方法中没有 self
mutating 方法 :值类型(结构体和枚举)默认方法是不能够修改属性的,加 mutating 关键字,能够变为改变方法。
类方法: static 关键字(结构体/枚举),class(类),不存在class编程
封装
把隐藏属性、方法和方法实现细节的过程称为封装
open:任什么时候候都能被继承,被访问
public 从外部模块和本模块均可以访问.在模块外不能被继承,只能被访问
internal:默认设置, 只有本模块能访问
private:只在当前类中使用
fileprivate:
多态:用父类的类型调用子类的方法
嵌套类型:在一个类型中嵌套定义另外一个类型swift
构造方法:
用于初始化属性
隐式调用
若是全部的存储属性都有默认值,能够不提供构造方法
若是存储属性能够提供缺省值,尽可能提供。简化代码
常量存储属性只容许经过缺省值和构造方法中被修改
可选值存储属性能够不在构造方法中初始化
结构体默认提供一个构造器
构造方法之间的相互调用称为构造器代理
经过闭包或全局函数设置存储属性的缺省值
懒加载是用到才执行,闭包赋值是初始化时就会执行
被 convenience 关键字修饰的构造方法称之为便利构造器。必定是经过调用其余构造方法来初始化,必定要出现self.init
派生类的构造方法 :
默认状况下构造方法不能被继承
基类的存储属性只能经过基类的构造方法初始化
初始化存储属性必须先初始化当前类再初始化父类
便利构造器必须调用同类中的其余构造器(指定或者便利)
调用子类构造器必定可以初始化全部属性
只有在调用完父类指定构造器以后才能访问父类属性
确保当前类和父类全部存储属性都被初始化
重写父类方法,加上 override 关键字,
便利构造方法不存在重写
子类的便利构造方法不能直接访问父类的方法,因此不存在重写的概念。
只要在构造方法的前面加上一个required 关键字, 那么全部的子类只要定义了构造方法都必须实现该构造方法后端
函数参数
内部函数
外部函数
第二个参数默认既是外部参数又是内部参数
能够在定义函数的时候给某个参数默认值,当外部调用没有传递该参数时自动使用默认值
默认状况下 Swift 中的参数都是常量参数,若是须要在函数中改变参数的值,须要添加 var 关键字
能够将 var 换成 inout,这会传递参数自己而不是参数的值api
函数类型
函数类型是由函数参数类型和返回值类型组成的。
能够利用函数类型定义函数变量和常量
函数类型能够做为函数的参数
函数类型能够做为函数的返回值数组
基本数据类型
整形
浮点型
长:Int8 Int 16 Int32 Int64
短
有符号:UInt
无符号:比有符号的取值范围更大。
swift 是类型安全的语言,取值错误的时候会直接报错。
oc中能够隐式类型转化,而在swift 中是不能够的安全
继承
新的类可以继承已有类的属性和方法,而且扩展新的能力
优势:代码重用
缺点:增长代码耦合性。
super 关键字
重写属性:不管是存储属性仍是计算属性,都只能重写为计算属性
属性观察器:willSet didSet
利用 final 关键字防止重写
能够修饰属性、方法和类
修饰的属性和方法不能被重写
修饰的类不能被继承
结构体
用于封装相同和不一样类型数据的,swift 中结构体是一类类型,能够定义属性和方法,甚至构造方法和析构方法。
若是结构体的属性有默认值,可使用()构造一个结构体
结构体有一个默认的逐一构造器,用于在初始化时给全部属性赋值。
若是没有,必须使用逐一构造器实例化
Swift 中结构体和其余面向对象语言同样都有构造函数,而 OC 没有的
结构体中的成员方法必须使用某个实例调用
结构体是值类型
赋值有两种状况
1.指向同一块存储空间
2.内容相同的两个不一样实例
结构体是值类型,结构体间的赋值就是拷贝一份数据,仍是不一样的实例
可选类型的本质实际上是一个枚举
None
Some
格式:Optional <类型>
因为可选类型在 Swift 中随处可见,因此系统作了这个语法糖,在类型后面加 ?
基本类型变量在使用以前必须初始化,不然报错
目的:安全,无论何时方法都是有意义的;
可选类型安全吗?安全,可使用可选绑定判断后再使用。
Swift 的发明者出于安全的考量,让咱们在使用基本类型的时候没必要考虑是否有值
Swift 中的可选类型变量更贴近于 oc 中的普通变量
可选链:经过可选类型变量来调用相应的属性和方法
1.强制解包。很是危险
2.经过可选绑定,代码繁琐,可是简单
3.经过可选链,若是 ?前面变量没有值,那么整个可选链会无效
可选链的返回值会自动包装成可选值
可选链调用下标索引,必须实现 subscript 方法,访问,实现get;赋值,实现set方法
判断赋值操做是否成功。判断返回值()? 或 Void? 都是表明成功
扩展
不须要知道目标源码,就能够给一个现存类、枚举、结构添加属性或者方法
限制条件:
1.不能添加已经存在的方法或属性
2.添加的属性不能是存储属性,只能是计算属性
格式:extension 某个先有类型 {}
扩展整数类型
扩展下标:
类
Swift 中结构体和类很是类似,但又有不一样之处
类是具备相同属性和方法的抽象
类没有逐一构造器
类是引用类型,结构体是值类型
恒等运算符。用于判断是不是同一个实例,也就是是否指向同一存储空间
类型转换
不容许隐式类型转换,可使用强制类型转换
枚举
Swift 中枚举要比 OC 中强大,由于它是一等类型,能够增长属性和方法
格式:
enum Method { case 枚举值}
枚举值能够连在一块儿写 case A, B, C, D.
可使用枚举类型常量或变量接收枚举值
利用 Switch 匹配, 若是 case 包含了全部的枚举值,不须要写 default,若是没有,必须写default。
原始值
oc 中枚举的本质就是整数,因此有原始值,从0开始
Swift 中的枚举是没有原始值的,能够在定义的时候告诉系统让枚举有原始值
enum Method:枚举值原始值类型 {}
Swift 中枚举值类型除了能够指定整型还能够指定其余类型,可是必须给全部枚举值赋值,由于不能自动递增
rawValue 表明将枚举值转换为原始值
hashValue 访问成员值对应的哈希值,不可改变,系统自动生成,Swift 在背后实际上使用哈希值来识别枚举符号。
经过原始值建立枚举值 Method(rawValue:),返回的是可选值,由于原始值对应的枚举值不必定存在,有可能为nil,因此最好使用可选绑定。
枚举相关值:可让枚举值相关的原始值是一个变量
Swift 内存管理
管理引用类型的内存,不会管理值类型,值类型不须要管理
内存管理原则:当没有强引用指向,就销毁
ARC 自动回收内存
弱引用,引用计数不变
若是利用weak 修饰变量,当对象释放后会被置为 nil
因此利用 weak 修饰的变量一定是一个可选类型。
unowned 无主引用,至关于 OC 中的 unsafe_unretained
unowned 和 weak 的区别
unowned 修饰的变量在释放以后不会置为nil,不安全
unowned 修饰的变量不是可选类型。
循环引用:ARC 不是万能的,它可以解决内存问题,但某些时候他不能解决内存泄漏问题。对象没有被销毁,可是咱们没有办法访问他们了。
当某一个变量或常量必须有值,一直有值,那么可使用 unowned 修饰
属性
存储属性:就是 oc 中的普通属性,在结构体和类中定义的属性,默认就是存储属性
常量存储属性:只能在定义或构造的时候修改,构造好以后不能修改
结构体和枚举是值类型
类是引用类型
不能修改结构体\枚举常量对象中的值
能够修改类中常量的值,不能够修改类常量的指向
延迟存储属性:swift 语言中当构造完一个对象后,对象中全部的存储属性都必需要有初始值,可是也有例外,其中延迟存储属性能够将属性的初始化推迟到该属性第一次被调用的时候
应用场景:
1.有可能不会用到
2.依赖于其余值
若是不是 lazy 属性,定义的时候对象尚未初始化,因此不能访问self
若是加上 lazy 属性,表明在使用时才加载,也就是在使用到属性的时候才会调用self
而访问一个类的对象必须使用对象方法,因此访问时对象已经初始化完成了。因此可使用self。
计算属性:
计算属性不直接存储值,没有任何的“后端存储与之对应”
用于计算,能够实现setter 和 getter 两种计算方法
枚举不可有存储属性,但能够有计算属性
计算属性不具备存储功能,因此不能直接赋值
setter 能够传递一个自定义的参数,也可使用系统默认的参数 newValue
若是使用系统默认参数,必须删除自定义参数。
只读计算属性
对应 oc 中的 readOnly,只提供 getter 方法。
只读属性必须是变量,不能是常量。
只读属性能够省略 get{}
应用:只能经过计算得到,不须要外界设置
属性观察器
相似 oc 中的kvo,能够用于监听属性何时被修改,只有被修改时才会调用。
willSet:在设置新值以前调用
didSet:在设置新值以后调用
能够为除计算属性和lazy属性以外的属性添加属性观察器,可是能够在继承类中添加父类计算属性的观察器
由于在计算属性中也能够监听到属性的改变,因此给计算属性添加观察期没有意义
类属性:
在结构体和枚举中使用static
在类中使用 class,而且在类中不容许将存储属性设置为类属性
普通属性是每一个对象一份
类属性是全部对象共用一份
数组
有值数组
空数组
不可变数组
可变数组
获取长度:.count
判断是否空:.isEmpty
检索: [index]
追加:.append or +=
插入: .insert(any:at)
更新: arr[0] = 0
删除:remove(at:) removeLast removeFirst removeAll(keepingCapacity:Bool)
Range: removeSubrange(0…1) replaceSubrange(0..<1, with:)
遍历:for number in array for i in 0..<array.count
取出数组某个区间的值 array[0..<3]
析构方法
对象的内容被回收前隐式调用的方法,至关于 oc 中的delloc
只要执行一些额外操做,例如释放一些持有资源、关闭文件、断开网络
deinit{}
父类的析构方法会被自动调用,不须要子类管理
subscript 下标:访问对象中数据的快捷方式
所谓下标脚本语法就是可以经过,实例[索引值]来访问实例中的数据
相似于访问数组和字典,Swift 中数组和字典其实就是结构体
要想实现下标访问, 必须实现 subscript 方法
下标访问要实现 get 方法
下标赋值要实现 set 方法
协议
定义协议: prtocol ProtocolName {}
协议能够继承一个或多个协议
结构体、枚举能够实现协议
类实现协议和继承父类,协议通常写在父类后面
协议的属性:
协议不指定是否该属性是一个存储属性仍是计算属性,只指定该属性的名称和读写属性。属性要求老是声明为变量属性,用 var 关键字作前缀。
协议普通方法实现
协议能够要求指定的实例方法和类型方法被一致的类型实现。这些方法被写为定义协议的一部分。不容许给协议方法参数指定默认值。
定义协议,指定方法要求
实现协议,实现方法
协议不只能够声明属性、方法、下标,还能够声明构造器。可是在 Swift 中除了某些特殊状况,构造器是不会被子类继承的。因此咱们须要在实现协议要求的构造器的时候,添加 required 关键字确保子类也得实现这个构造器。
举个简单的栗子,有一只猫和狗,他们都属于宠物,用类实现的时候,定义一个父类叫宠物,里面有喂食和玩耍两个方法,猫和狗都继承于宠物这个父类。这样操做天然能够实现。可是要知道,猫和狗并不彻底是宠物,这里把宠物当作父类就不是很合适,这里应该把宠物定义成协议就合适不少了
宠物猫和宠物狗,利用协议能够这样实现。定义一个 Animal 类,让猫和狗都继承于 Animal。再定义一个宠物的协议,让猫和狗都实现协议。
同时继承父类和协议的时候,父类要写在前面。
swift 是面向协议编程。是由于 swift 中协议的强大。
与面向对象的不一样:
面向对象: 动物-宠物-猫。缺点:宠物可能不须要动物的一些属性。
面向协议:动物-猫 协议:宠物
面向协议编程:从协议出发
extension 协议 能够给协议的方法增长默认实现
typealias 与协议结合的使用
typealias 的做用是给类型进行扩展,它与协议放在一块儿会碰撞出不同的火花。
协同协议的使用
CustomStringConvertible 用于自定义打印
Comparable 协议用于自定义比较符号
Equatable 协议用于自定义“==”
协议是 Swift 很是重要的一部分,苹果甚至为了他单独出来—面向协议编程,利用协议的优势和灵活性可使项目的架构更加灵活,拥有更加易于延展的架构。
元祖
在其余语言中很早就是有元祖这个概念,可是对于 oc 程序员这是一个新的概念
将多个相同或者不一样类型的值用一个小括号括起来就是一个元祖
元祖其实和结构体很像,只是不须要提早定义类型
元祖实际上是复合类型,小括号内能够写任意类型
元祖的其余定义方式:
运算符
算术运算符
Swift 是安全严格的编程语言,会在编译时候检查是否溢出,可是只会检查字面量,不会检查变量,因此在 Swift 中必定要注意隐式溢出。
Swift 中不容许连续赋值
区间:
闭区间 a…b
半闭区间: a..<b
区间只能用于整数,写小数有问题
字典
key 必定是能够 hash 的(String, Int, Double, Bool),
哈希就是将字符串变成惟一的整数,便于查找,提升字典遍历速度
获取 key 对应的值增长 ??
字符串
计算字符串长度:.lengthOfBytes(using:String.Encoding.utf8)
字符串拼接: stringA + stringB
格式化字符串 (format)
字符串比较:== / != / >= / <=
判断先后缀:hasPrefix hasSuffix
大小写转换:uppercased lowercased
转换基本数据类型:强转
字符:Character
Swift 使用双引号
双引号下只能放一个字符。
oc 中字符串是以\0结尾的。
Swift 并非
break :跳出循环,不管循环条件是否保持还为真都不会再执行循环
continue:跳出本次循环,若是循环条件还为真还会继续执行循环
for
stride(from:to:by)遍历,跨步,开区间
stride(from:through:by)遍历,跨步,闭区间
Swift4 语法
struct 也支持 kvc
KVO: 目前依然只有 NSObject 才支持 KVO
被观察的属性须要用 dynamic 修饰,不然也没法观察到
断言:当程序发生异常时,若是但愿找到出错位置并打印一条消息,就可使用断言,即经过一个全局的函数 assert
assert 接受一个闭包作为其第一个参数,第二个参数是一个字符串。加入第一个闭包的返回值是false,那么这个字符串就会被打印到中控制台上。
assert(()->Bool.”Message”)
precondition:它和 assert 的格式类型,也是动态的,它会形成程序的提早终止并抛出错误信息;
使用扩展的方法向数组增长一个方法判断下标是否越界。
precondition 在通常代码中并不常见,由于它是动态的,只会在程序运行时进行检查。适用于那些没法在编译器肯定的风险状况
switch
在 case 中定义变量要加大括号,不然做用域混乱
能够判断对象类型,不能够穿透,不能不写 default,位置必须在最后;定义变量不须要加大括号
区间
元祖匹配
值绑定
根据条件绑定
while
swift 不须要 import 类名也可使用这个类
参数名称缩写
Swift 自动为内联闭包提供参数缩写功能,你可使用 $0,$1, m2 依次获取闭包的第1,2,3个参数
CGRect CGRectDivide
将一个 CGRect 在必定位置切分红两个区域
CGRectDivede(rect,&small,&large,20,CGRectMinXEdege)
将 rect 分割成两部分,{0,0,20,100} {20,0,80,100}
swifter 王巍
swift 新元素
柯里化(Currying)
若是你固定某些参数,你将获得接受余下参数的一个函数
函数式编程在把函数当作一等公民的时候,就不可避免的会产生“柯里化”这种用法
柯里化是量产类似方法的好办法
protocol 的方法声明为 mutating
若是没在协议方法里写 mutating 的话,别人若是用 struct 或 enum 实现这个协议的时候就没法在方法里面改变本身的变量了
Sequence
swift 中 for in 这种方式的遍历是针对 sequenceType 这个 protocol 的。为了实现 Sequence 首先须要实现一个 IteratorProtocol。
Sequence 协议扩展已经实现了 map,filter,reduce
map 方法 能够用在 Optionals 和 SequenceType 上,能够把数组元素转变类型
filter 用于选择数组元素中知足某种条件的元素
reduce 用于把数组元素组合计算为一个值
flatMap 返回一个将变换结果链接起来的数组(压平)。空值过滤。
多元组(Tuple)
应用:交换输入 (a, b) = (b, a)
let rect = CGRect(x:0,y:0,width:100,height:100)
let (small,large) = rect.divided(atDistance:20,from: .minXEdge)
@autoclosure 和 ??
@autoclosure 能够说是 Apple 的一个很是神奇的创造,由于这更多的像是在“hack”这门语言
就是把一句表达式自动的封装成一个闭包,这样有时候在语法上看起来就会很漂亮。
logIfTrue(2>1)
不支持带有输入参数的写法
?? 这个操做符能够判断输入并在当左侧的值是非 nil 的 Optional 值时返回其 value,不然返回右侧的值。
内部实现:defaultValue 就是一个 @autoclosure 修饰的参数。
为何不直接传入 T 参数?
是由于若是这个默认值是通过一系列计算获得的话,可能会形成浪费。
为何会浪费?
是由于当 optional 不是 nil 的话,咱们其实是彻底没有用到这个默认值。而是直接返回解包以后的值。这样的开销是彻底能够避免的。方法既是将默认值的计算推迟到了 optional 断定为 nil 以后
@escaping
最简单的 block 作为参数的调用,默认隐藏了一个假设,就是参数中 block 会在 方法返回前完成。也就是说对 block 的调用是同步行为。若是咱们改变一下代码,将 block 放到一个 dispatch 中,让它在方法返回后被调用的话,咱们就须要在 block 类型前面加上 @escaping 标记来代表这个闭包是会逃逸出该方法的。
没有逃逸行为的闭包,由于做用域就在函数内部,因此不用担忧闭包内部持有 self。
有逃逸行为的闭包,Swif 强制咱们写明 self,以起到提醒做用,咱们就须要考虑 self 的持有状况。若是咱们不但愿在闭包中持有 self,可使用 【weak self】的方式来表达。
这时,在闭包执行时已经没有对实例的应用,所以输出为 nil。
Optional Chaining 可选链
Optional Binding 可选绑定
使用可选链可让咱们摆脱不少没必要要的判断和取值
由于可选链随时均可能提早返回 nil ,因此使用 optional chaining 所获得的东西都是 optional 的。
使用 optional Binding 来断定方法是否调用成功
操做符
与 OC 不一样,swift 支持重载操做符这样的特性。最多见的使用方式就是定义一些简便的计算。好比二维向量的数据结构的操做。
若是想要定义一个全新的操做符,须要本身来作声明。
precedencegroup:操做符优先级别
associativity:结合律
higherThan:运算的优先级
infix:表示定义的是一个中位运算符,即先后都是输入。
Swift 的操做符是不能定义在局部域中的,由于至少为但愿能在全局范围内使用你的操做符,不然操做符也就失去意义了.
在重载或者自定义操做符的时候。应当尽可能将其做为其余方法的“简便写法”,避免在其中实现大量逻辑或提供独一无二的功能。
来自不一样 module 的操做符是有可能冲突的,在使用重载或者自定义操做符时,请先再三权衡斟酌,你或你的用户是否真的须要这个操做符。
func 的参数修饰
Swift 是一门讨厌变化的语言,全部有可能的地方,都被默认为是不可变的。这样不只能够确保安全,还能够在编译器的性能优化上更有做为。
有时候咱们但愿在方法内部直接修改输入的值,这时候咱们能够用 inout 来对参数进行修饰。在前面加上 & 符号。
值类型,并不能直接修改它的地址来让它指向新的值。
字面量表达
指像特定的数字、字符串或者是布尔值这样,可以直截了当指出本身类型并为变量赋值的值。
在 Swift 中,Array 和 Dictionary 在使用简单的描述赋值的时候,使用的也是字面量。
Swift 为咱们提供了一组很是有意思的协议,使用字面量来表达特定的类型。对于那些实现了字面量表达协议的类型,在提供字面量赋值的时候,就能够简单de按照协议方法中定义的规则“无缝对应”的经过赋值的方式将值表达为对应类型。
下标
在绝大多数语言中使用下标读写相似数组或者字典这样的数据结构。
在字典中使用下标访问获得的结果是一个 optional 的值。
作为一门表明了先进生产力的语言,Swift 是容许咱们自定义下标的。不只包含对本身写的类型进行下标定义,也包含那些已经实现下标访问的类型进行扩展。
向数组添加一个接受数组下标输入的读取方法。不推荐使用这样的方式。
方法嵌套
方法成为了一等公民,这样就能够把方法作为变量或者参数来使用了。咱们能够在方法内定义新的方法,这个代码结构层次和访问级别的控制带来了新的选择。
另外一个重要的考虑是有些方法咱们彻底不但愿在其余地方被直接使用。
命名空间
oc 一直以来使人诟病的地方就是没有命名空间,在应用开发时,全部的代码和引用的静态库都会被编译到同一个域和二进制中。这样的后果一旦咱们有重复的类名的话,就会致使编译时的冲突和失败。
在 Swift 中可使用命名空间了,即便是名字相同的类型,只要是来自不一样命名空间的话,就会和平共处的。Swift 的命名空间是基于 module 而不是在代码中显示地指明,每一个 module 表明了 Swift 中的一个命名空间。
typealias
为已经存在的类型从新定义名字。可使用 typealias ,给类型取一个别名,增长代码的可读性。能够再也不关心代码里那成堆的 Int 和 string 之类的基本类型表明的是什么东西。
typealias 是单一的,不能将整个泛型类型进行重命名。
另外一个使用场景是某个类型同时实现多个协议的时候,咱们能够用 & 符号链接协议。而后给一个新的更符合上下文的名字,加强代码可读性
typealias Pet = Cat & Dog
associatedtype
使用 associatedtype 能够在协议中添加一个限定,来指定事物的具体类型。在 Tiger 经过 tyoealias 具体指定 F 为 Meat 以前, 只须要知足协议的类型中 F 和 eat 参数一致便可。不过这里忽视了被吃的必须是 Food 类型这个前提。associatedtype 声明中可使用冒号来指定类型知足某个协议。另外,在 Tiger 中只要实现了正确类型的 eat,F 的类型均可以推算出来,不须要显式的写明 F。
在添加 assoicatedtype 以后,Animal 协议就不能被当作独立的类型使用了。这是由于 Swift 在编译的时候须要肯定全部类型。在一个协议加入了 associatedtype 或者 Self 的约束以后,它将只能被用为泛型约束,而不能作为独立类型的占位使用,也是去了动态派发的特性。
可变参数函数
就是能够接受任意多个参数的函数。咱们最熟悉的就是 NSString 的 stringWithFormat: 方法了。
在 Swift 中写一个可变参数的函数只须要在声明参数时在类型后面加上 … 就能够啦。输入的参数在函数体内部将被作为数组来使用。
在 Swift 中能够随意的放置可变参数的位置,而没必要拘泥于最后一个参数。
在同一个方法中只能有一个参数是可变的,可变参数都必须是同一个类型的。
数组的两个便利方法:forin forEach
当遍历的时候有 return 关键字的时候就会有区别
初始化方法顺序
设置本身须要初始化的参数
调用父类相关的初始化方法
对父类中的须要改变的成员进行设定
若是没有第三步,第二步也能够省略。默认最后调用第二步。
Swift 中的初始化想要达到什么样的目的。
其实就是安全。Swift 有超级严格的初始化方法。不加修饰的 init 方法都须要在方法中保证全部非 Optional 的实例变量被赋值初始化,而在子类也强制(显示或隐式)调用 super 版本的 designated 初始化,因此不管如何走何种路径,被初始化的对象老是能够完成完整的初始化。
在 init 方法里面咱们能够对 let 的实例常量进行赋值,这是初始化的重要特色.这是由于在 Swift 中 init 方法只会被调用一次。
在 init 前面加上 convenience 关键字的初始化方法是 Swift 中的“二等公民”,全部的 convenience 的方法都必须调用同一个类中的 designated 初始化完成设置,另外,convenience 方法不能被子类重写也不能从子类使用 super 调用
只要在子类中实现了父类 convenience 所需的 init 方法,咱们在子类中就可使用父类的 convenience 的初始化方法了。
对于某些咱们但愿子类中必定实现的 designated 方法,能够经过添加 required 关键字进行限制,强制子类对这个方法重写实现。能够保证依赖于某个 designated 初始化方法的 convenience 一直能够被使用。
对于 convenience 方法咱们也能够加上required 以确保子类对其实现。这在要求子类不直接使用父类中的 convenience 初始化方法时有帮助。
初始化返回 nil
Swift 中咱们能够在 init 声明时在其以后加上 ? 或者 !来代表初始化失败可能会返回一个 nil。全部的结果都将是 Optional 类型,咱们能够经过 Optional Binding,就能知道是否初始化成功,并安全的使用它们了。 咱们在这类初始化方法中还能够对 self 赋值,也算是 init 方法里的特权之一
static 和 class
表示“类型范围做用域”。类变量/类方法,静态变量/静态函数。非 class 中,使用 static 来描述类型做用域。包括在 enum 和 struct 中描述类型方法和类型属性时。在这两个值类型中,咱们能够在类型范围内声明并使用存储属性、计算属性和方法。
class 关键字是专门用在 class 类型的上下文中的。用来修饰类方法和类计算属性。但不能出现 class 的存储属性。
若是咱们想在 protocol 中实现一个类型域上的方法或者计算属性的话,应该用 static 关键字。实现的时候,无论在什么结构类型中,使用 static 是没有问题的。
多类型和容器
Swift 中原生容器类型有三种。它们分别是 Array、Dictionary 和 Set;
它们都是泛型的,也就是说咱们在一个集合中只能放入同一个类型的元素。
若是咱们要把不相关的类型放入到同一个容器中的话,须要作一些转化工做。
这样的转换会形成部分信息的缺失,咱们从容器中取值时只能取到信息彻底丢失以后的结果。在使用时还须要进行一次类型转换。这实际上是无其余可选方案后的最差选择,由于这样的转换以后,编译器就不能再给咱们提供警告信息了。咱们能够随意添加,也能够将取出的值随意转换,这是一件很是危险的事情。
咱们还能够在容器中添加实现了同一协议的类型的对象,对于对象中存在某种共同特性的状况下无疑是最方便的。
另外一种方式是利用 enum 能够带有值得特色,将类型信息封装到特定的 enum 中。
default 参数
Swift 的方法是支持默认参数的,也就是在声明方法的时候,能够给参数制定一个默认使用的值。
在调用的时候,咱们若是想要使用默认值的话,只要不出入相应的值就能够了。
默认参数写的是 default,这是含有默认参数的方法所生成的 Swift 的调用接口。当咱们指定一个编译时就能肯定的常量作为默认参数的取值的时候,这个取值是隐藏在方法内部,而不该该暴露给其余部分。
举个例子:NSLocalizedString(key:String, tableName:String? = default,..)
assert(@autoclosure condition:()->Bool,@autoclosure _ message:()->String = default..)
正则表达式
做为一门先进的编程语言,Swift 能够说是吸取了众多其余先进语言的特色,可是让人略微失望的,就是 Swift 至今没有在语言层面上支持正则表达式。就像 Perl 或者 Ruby 那样的语言使用好比 =~ 这样的符号来进行正则匹配。
最简单的想法天然是自定义 =~ 这个运算符。在 Cocoa 中咱们可使用 NSRegularExpression 来作正则匹配。咱们能够先写一个接受正则表达式的字符串,以今生成 NSRegularExpression 对象,而后使用该对象来匹配输入字符串,并返回结果告诉调用者是否匹配成功。
模式匹配
Swift 中的模式匹配只能支持最简单的相等匹配和范围匹配。使用 ~= 来表示模式 匹配的操做符。
在操做符两边分别接受能够判等的类型,能够与 nil 比较的类型,已经一个范围输入和某个特定值,返回值很明了,都是是否匹配成功的 Bool 值。
Swift 的 switch 就是使用了 ~= 操做符进行模式匹配。在 switch 中作 case 判断的时候,咱们彻底可使用自定义的模式匹配方法来进行判断。
… 和 ..<
在不少脚本语言中,都有不少相似 0..3 或者 0…3 这样的 Range 操做符,用来简单指定一个从 X 开始连续计数到 Y 的范围。
最基础的用法固然是在两边指定数字。0…3 表示从 0 开始 到 3 为止包含 3 这个数字的范围,成为全闭合的范围。 0..< 3 是不包含最后一个数字的范围。对于这样获得的数字的范围,咱们能够对他进行 for…in 的访问。
看看 Swift 对这两个操做符的定义,能够发现都是支持泛型的。能够接受 comparable 的输入,返回 ClosedInterval 或者 HalfOpenInterval 。在 Swift 中除了数字另外一个实现 comparable 的基本类型就是 String。
例子:
确认某个单词所有字符都是小写英文字母 interval = a…z
是否是有效的 ASCII 字符 \0…~ ,分别是 ASCII 的第一个和最后一个字符。
AnyClass,元类型和 .self
在 Swift 中可以表示“任意”这个概念的除了 Any 和 AnyObject 外,还有 AnyClass。
AnyClass 在 Swift 中被一个 typealias 定义:typealias AnyClass = AnyObject.Type
。这样获得的是一个元类型。在声明时咱们老是在类型后面加上 .Type。表明这个类型的类型。从 A 中取出其类型的时候,咱们须要用到 .self。
元类型或者元编程在咱们编写某些框架性的代码时很是方便。能够经过读入配置文件之类的方式实现。
DSL:领域专属语言的方式。不触及源码的状况下,很简单的完成一系列复杂的操做。
在注册 tableview 的 cell 的类型的时候就须要输入 AnyClass 。
得到协议的元类型:在protocol 的名字后面使用 .Protocol 获取。
协议和类方法中的 Self
在声明协议的时候,咱们但愿在协议中使用的类型就是实现这个协议自己的类型的话,就须要使用 Self 进行指代。
使用 type(of:)初始化,保证方法与当前类型上下文无关。
在类方法中也可使用 Self。核心在于保证子类也能返回恰当的类型。
动态类型和多方法
Swift 中咱们虽然能够经过 dynamicType 来获取一个对象的动态类型,可是在使用中,Swift 如今是不支持多方法,不能根据对象在动态时的类型进行合适的类型方法调用。方法的调用只在编译时决定。
Swift 咱们能够重载一样名字的方法,而只须要保证参数类型不一样。
属性观察
利用属性观察咱们能够在当前类型内监视对于属性的设定。Swift 为咱们提供两个属性观察的方法,willSet 和 didSet。
属性观察的一个重要用处是做为设置值的验证。
存储属性会在内存中实际分配地址对属性进行存储,而计算属性则不包括背后的存储,只是提供 set 和 get 两种方法。
在同一类型中属性观察和计算属性是不能同时共存的。若是咱们没法改动这个类,又想要经过属性观察作一些事情的话,可能就须要子类化这个类,而且重写它的属性。重写的属性并不知道父类属性的具体状况,只是继承了属性的名称和类型,所以在子类的重载属性中咱们是能够添加对父类属性的属性观察,而不用在乎父类中的属性究竟是计算属性仍是存储属性。
didSet 中会用到 oldValue,而这个值须要在 set 以前获取并存储,不然没法保证正确性。
final
final 关键字能够用在 class 、func 、var 前面进行修饰,表示不容许对该内容进行继承或重写操做。
为了父类中某些代码必定会被执行。
有时候父类的方法在被继承以后必须执行的,若是子类重写了父类的方法,是没有办法强制子类方法必定会去调用相同的父类方法的。
子类继承和修改是一件危险的事情
lazy 修饰符和 lazy 方法
咱们在使用 lazy 作为属性修饰符时,只能声明属性是变量,而且显式的指定属性类型,还有一个给属性赋值的语句在首次访问属性时运行。
在 Swift 标准库中,还有一组 lazy 方法。这些方法能够配合 像 map 或 filter 这类接受闭包并进行运行的方法一块儿,让整个行为变成延迟执行。
隐式解包 Optional
相对于普通的 Optional ,在 Swift 中还有一种特殊的 Optianal 类型,对他的成员或者方法进行访问的时候,编译器会帮咱们自动进行解包,也就是 ImplicitlyUnwrappedOptional。在声明的时候咱们能够在类型后面加上一个(!)来告诉编译器咱们须要一个能够隐式解包的 optional 值
若是 这个 optional 值是 nil 的话不加检查的写法调用会致使程序奔溃。这是一种危险写法
由于 oc 的 Cocoa 的全部类型变量均可以指向 nil,在将 Cocoa API 从 oc 转移到 Swift 时,没法断定是否存在 nil 的可能。这时候 ,Optional 隐式解包就做为一种妥协方案出现了。使用隐式解包的最大好处是对于那些咱们能肯定的 api 来讲,咱们可直接访问属性和调用方法,会很方便。这是一种简单可是危险的使用方式了。
如今比较常见的隐式解包就只有 IBOutlet 了
少用 隐士解包 optional ,推荐多写 optional binding。
多重 Optional
optional 类型是一个 enum。Optional
若是咱们把 optional 比做一个一个盒子,那么打开这个盒子以后可能的结果会有空气、糖果,或者是另外一个盒子。
使用 fr v -R 能够打印 optional 值
Optional Map
map 方法能够在 collectionType(这是一个协议) 的 extension 中找到定义
在其余语言中 map 是常见经常使用的一种语言特性了。
在 optional 的声明中,也有一个 map 方法。这个方法可让咱们方便的对可选值作变化和操做,而不用手动解包。
正符合函数式编程函子的概念。函子指的是能够被函数使用,并映射为另外一组结果,而这组结果也是函子的值。
Protocol Extension
在 Swift 中标准库的功能都是基于 protocol 来实现的,举个例子,Array 就是遵照了 collectionType 这个协议的。 CollectionType 是一个很是重要的协议,除了 Array 之外,Dictionary 和 Set 也都实现了这个协议所定义的内容。
为实现了某个协议的全部类型添加一些另外的共通的功能。
咱们能够对一个 Protocol 进行扩展,而扩展中实现的方法将做为实现扩展的类型的默认实现。在具体的实现这个协议的类型中,即便什么都不写,也能够编译经过。调用的时候会直接使用 extension 中的实现。
protocol extension 为 protocol 中定义的方法提供了一个默认实现。
where 和模式匹配
在 switch 语句中,使用 where 来限定某些条件 case,这能够说是模式匹配的标准用法。
在 for in中 可使用 where 作相似的条件限定
在 Swift 3 中,if let 和 guard let 的条件判断再也不使用 where 语句,而是和普通的条件判断同样,用逗号写在后面。
这两种方式均可以用另外的 if 来替代,只不过让咱们的代码更加的易读了。也有一些场合只有 where 才能准确表达的。好比咱们在泛型中想要对方法的类型进行限定的时候。
例如 :标准库里对 RawRepresentable 协议定义 != 运算符。
当咱们但愿一个协议扩展的默认实现只在一些特定的状况下适用,咱们就能够用 where 关键字来进行限定。
例如:Sequence 的 sorted 方法就被限定在这样一个类型限定的协议扩展中。
indirect 和嵌套 enum
涉及到数据结构的理论和模型(链表、树、图)时,咱们每每会用到嵌套的类型。
2.从 Objective-C 到 Swift
Selector
@selectior 是 oc 时代的关键字,它能够把一个方法转换为 SEL 类型,它的表现很相似一个动态的函数指针。
若是要追求灵活的话,咱们更愿意用 NSSelectiorFromString ,由于咱们能够在运行时动态生成字符串,从而经过方法的名字调用到方法。
在 Swift 中,咱们用 #selector 从暴露给 oc 的方法中获取方法名字。对应原来的 SEL 是一个叫 selector 的结构体。
须要注意的是,selector 实际上是 OC runtime 的概念。若是你的 selector 只对 Swift 可见的话(也就是一个 private 方法),在调用这个方法的时候你会遇到一个 unrecognized selector 的错误。正确的方法是在 private 前面加上 @objc 关键字,这样在运行时就能够找到方法了。
值得一提的是,若是方法名字在方法所在域内是惟一的话,咱们能够只是用方法名字作为 #selector 的内容。相比于带有冒号写法的完整形式来讲,这么写起来会方便一些。
可是若是同一个做用域中存在相同名字的方法,咱们可使用强制转化来区分它们。
实例方法的动态调用
在 Swift 中有这样一种写法,能够不直接使用实例来调用这个实例的方法,而是经过类型取出这个类型的某个实例方法的签名,而后再经过传递实例来拿到实际须要调用的方法。
Swift 能够用 Type.instanceMethod 的语法来生成一个柯里化的方法。
若是遇到有类型方法的冲突时,默认取到的是类型方法,若是想要取得实例方法,能够显式声明类型。
单例
对于一些但愿能在全局方便访问的实例,或者在 app 的生命周期内只应该存在一个的对象,咱们通常使 用单例进行存储和访问。
在 oc 中使用 GCD 保证代码只执行一次。保证单例在线程上的安全。
能够用dispatch_once 保证线程安全,可是在 Swift 中其实有一种更简单的保持线程安全的方式,就是 let。
Swift 1.2 以前不支持 static let 和 static var 这样的存储类变量。在 1.2 中支持了类变量。
在初始化变量的时候,Swift 会把初始化包装在 swift_once_block_invoke 中,保证惟一性。
在类型中加入了一个私有的初始化方法,来覆盖默认公开的初始化方法,这使的项目中的其余地方不能经过 init 初始化单例实例,保证了类型单例的惟一性。
若是你须要的是相似 default 的形式的单例(也就是能够建立本身的实例),能够去掉这个私有的 init 方法。
条件编译
在 c 系语言中,可使用 #if #ifdef 之类的编译条件分支来控制哪些代码须要编译。Swift 没有宏定义的概念,所以不能用 #ifdef 方法来检查某个符号是否通过宏定义。
编译标记
// MARK: 粗体标签的形式将名称显示在导航栏
// TODO:还没完成
// FIXME:需修改
Swift 中没有相似 OC 的 #warning
@UIApplicationMain
调用方法,根据第三个参数初始化一个 UIApplication 或其子类的对象并开始接受事件(传入nil。默认 UIApplication),最后一个参数指定了 Appdelegate 做为应用的代理。用来接收 didFinishLaunching 或者 didEnterBackground 这样的与应用生命周期相关的委托方法。
Swift 中在默认的 Appdelegate 类的声明上方有一个 @UIAppdelegateMain 的标签。这个标注作的事情就是将标注的类做为委托。建立一个UIapplication并启动程序。
Swift 中也能够有一个 main.swift 的文件,不须要定义做用域,直接书写代码。这个文件中的代码将做为 main 函数执行。
咱们能够将第三个参数替换为咱们本身的 UIApplication 子类,这样咱们就能够轻易作一些控制整个应用行为的事情了。
例如:每次发送事件(点击按钮),咱们均可以监听到这个事件了。
@objc 和 dynamic
虽说 Swift 的初衷是摆脱 oc 沉重的历史包袱,可是不能否认的是,通过二十多年的洗礼,Cocoa 框架早就烙上了不可磨灭的 oc 的印记。无数的第三方库是 oc 写成的,这些积累不容小觑。因此 Swift 作了与 oc 的兼容
Apple 的作法是容许咱们在同一个项目中同时使用 Swift 和 oc 开发。其实一个项目中的 Swift 和 oc 是处于两个世界中的,为了可以让他们可以互通,咱们须要添加一些桥梁。
经过添加{product-module-name}-Bridging-Header.h 文件,并在其中填写想要使用的头文件名称,咱们就能够在 Swift 中使用 oc 代码了。
若是想要在 oc 中使用 Swift 代码,能够直接导入自动生成的头文件 {product-module-name}-Swift.h 来完成。
oc 对象是基于运行时,骨子里遵循了 KVC 和动态派发,在运行时再决定具体实现。Swift 为了追求性能,类型和方法在编译时就已经决定,运行时再也不须要通过一次查找,能够直接使用。
在 Swift 类型中,咱们能够把须要暴露给 oc 的任何地方加上 @objc 修饰符。
可选协议和协议扩展
Swift 中的 protocol 的全部方法都必须被实现,那些若是没有实现协议就没法正常工做的方法通常是必须的,而相对的像作为事件通知或对非关键属性进行配置的方法通常都是可选的。
若是咱们想要将 Swift 像 oc 那样定义可选的协议方法,就须要将协议自己和方法都定义成 oc 的。也就是在 protocol 定义以前已经协议方法以前加上 @objc。和 oc 不一样的是咱们使用 optional 来定义可选方法。
对于全部的声明,它们的前缀都是分开的,也就是说你不能像在 oc 里那样用一个 @optional 指定接下来的若干个方法都是可选的了。必须对每个可选方法添加前缀,对于没有前缀的方法来讲,它们是默认必须实现的。
一个不可避免的限制是,使用 @objc 修饰的 protocol 就只能被 class 实现了。对于 struct 和 enum 来讲。咱们是没法让他们所实现的协议中含有可选方法或者属性的。另外,实现它的 class 中的方法还必须被标记为 @objc。或者整个类继承自 NSObject。
在 Swift 2.0 后,咱们有了另外一种选择,那就是使用 protocol extension。咱们能够在声明一个 protocol 以后再用 extension 的方式给出部分方法的默认实现。这样这些方法在实际的类中就是可选实现了。
内存管理,weak 和unowned
Swift 是自动管理内存的,初始化建立一个对象时,替咱们管理和分配内存。释放的原则遵循自动引用计数的原则:当一个对象没有引用的时候,其内存将会被自动回收。这套机制从很大程度上简化了咱们的代码,咱们只要保证在合适的时候将引用置空,就能够确保内存不会出错。
可是全部自动引用计数机制都有一个没法绕过的限制,那就是循环引用。
在 Swift 里防止循环引用,咱们必须给编译器一点提示,表示咱们不但愿它们互相持有。通常来讲咱们但愿“被动”的一方不要去持有“主动”的一方。
unowned 更像是 oc 的 unsafe_unretained。unowned 设置之后即便它原来引用的内容已经被释放了,它仍然会保持对被已经释放了的对象的一个“无效的”引用。他不能是 optional 值,也不会指向 nil。若是你尝试调用这个引用的方法或者访问成员属性的话,程序就会崩溃。而 weak 就友好些,在引用的内容被释放以后,标记为 weak 的成为会被置为 nil(因此被标记为 weak 的变量必定是 optional ),关于二者的选择,Apple 给咱们的选择是若是能确保使用时不会被释放,尽可能使用 unowned,若是存在被释放的可能,那就选择用 weak。
闭包循环引用。若是咱们能够肯定在整个过程当中 self 不会被释放的话,能够把 weak 改成 unowned ,这样能够不判断 StrongSelf。
这种在闭包参数的位置进行标注的语法结构是将要标注的内容放在参数的前面,并使用中括号括起来。若是有多个须要标注的元素,用逗号隔开。
@autoreleasepool
Swift 在内存管理上使用的是自动引用计数的一套方法,在 ARC 下虽然不须要手动的调用 retain, release 这样的方法来管理引用计数,可是这些方法仍是会被调用,只是编译器在编译时帮咱们在合适的地方加入了而已。
在 app 中,整个主线程是在自动释放池中运行的,
autoreleasepool 的使用状况。1.再循环中一直不断建立 autorelease 对象。能够再循环中加入释放池。可是每一次循环都生成自动释放池,虽然能够保证内存使用达到最小,可是释放过于频繁可能回带来性能忧虑。
在 swift 中更提倡用初始化的方法而不是用类方法来生成对象。使用初始化的话,咱们就不须要面临自动释放的问题。每次超过做用域,自动内存管理将会为咱们作好内存相关的事情。
值类型和引用类型
Swift 的类型分为值类型和引用类型两种,值类型在传递和赋值时将进行复制,而引用类型则只会使用引用对象的一个“指向”。struct 和 enum 是值类型,class 是引用类型。Swift 中的内建类型都是值类型,不只包括传统意义像 Int,Bool,甚至 String, Array, Dictionary 都是值类型。
使用值类型的好处。一个显而易见的优点就是减小了堆上内存分配和回收的次数。Swift 的值类型,特别是数组和字典这样的容器,在内存管理上通过了精心的设计。值类型的一个特色是在传递和赋值时进行复制,确定会产生额外开销,可是在 Swift 中这个消耗被控制在了最小范围,在没有必要复制的时候,值类型的复制都是不会发生的。也就是说,简单的复制,参数的传递等等普通的操做,虽然咱们可能用不一样的名字来回设置和传递值类型,可是在内存上他们都是同一块内容。
值类型被复制的时机是值类型的内容发生改变时。
值类型在复制时,会将存储在其中的值类型一并进行复制,而对于其中的引用类型来讲,则只复制一份引用。
虽然将数组和字典设置为值类型最大的考虑是为了线程安全,可是这样的设计在存储的元素或条目较少时,给咱们带来一个优势,就是很是高效。可是在少数状况下,当容器内容比较多,而且还要对内容进行增长或删除,在这时,Swift 内建的值类型的容器在每次操做的时候都要复制一遍。这个开销就就不容忽视。幸亏咱们有 Cocoa 中的引用类型来应对,就是 NSMutable。
String 仍是 NSString
像 String 这样的 Swift 类型对于 Foundation 对应的类是能够无缝转换的。
Swift 中 string 是 struct 类型,相比起 NSObject 的 NSString 类来讲,更切合字符串的不变特性。
使用 String 惟一一个比较麻烦的地方在于他和 Range 的配合。在使用 String 对应的 API ,NSRange 会被映射成他在 Swift 中且对应 String 的特殊版本:Range<String.Index>.这时候就很是讨厌。这种状况下,将 String 转为 NSString 是个不错的选择
UnsafePointer
不安全的指针
指针在 Swift 中并不被提倡,语言标准中也是彻底没有与指针彻底等同的概念的。为了与庞大的 c 系帝国进行合做,Swift 定义了一套对 C 语言指针的访问和转换方法。就是 UnsafePointer 和他的一系列变体。对于使用 C API 时若是遇到接受内存地址作为参数,或者返回是内存地址的状况,在 Swift 中会转为 UnsafePoin
const 对应 UnsafePointer,可变对应 UnsafeMutablePointer
在 C 中,对某个指针进行取值使用 *,在 Swift 中使用 meemory 属性读取相应内存中存储的内容。经过传入指针地址进行方法调用就比较相似了,都是 &。
C 指针内存管理