版本记录
版本号 | 时间 |
---|---|
V1.0 | 2017.08.01 |
前言
我是swift2.0的时候开始接触的,记得那时候还不是很稳定,公司的项目也都是用oc做的,并不对swift很重视,我自己学了一段时间,到现在swift3.0+已经出来了,自己平时也不写,忘记的也差不多了,正好项目这段时间已经上线了,不是很忙,我就可以每天总结一点了,希望对自己对大家有所帮助。在总结的时候我会对比oc进行说明,有代码的我会给出相关比对代码。
1. swift简单总结(一)—— 数据简单值和类型转换
2. swift简单总结(二)—— 简单值和控制流
3. swift简单总结(三)—— 循环控制和函数
4. swift简单总结(四)—— 函数和类
5. swift简单总结(五)—— 枚举和结构体
6. swift简单总结(六)—— 协议扩展与泛型
7. swift简单总结(七)—— 数据类型
8. swift简单总结(八)—— 别名、布尔值与元组
9. swift简单总结(九)—— 可选值和断言
10. swift简单总结(十)—— 运算符
11. swift简单总结(十一)—— 字符串和字符
12. swift简单总结(十二)—— 集合类型之数组
13. swift简单总结(十三)—— 集合类型之字典
14. swift简单总结(十四)—— 控制流
15. swift简单总结(十五)—— 控制转移语句
16. swift简单总结(十六)—— 函数
17. swift简单总结(十七)—— 闭包(Closures)
18. swift简单总结(十八)—— 枚举
19. swift简单总结(十九)—— 类和结构体
20. swift简单总结(二十)—— 属性
21. swift简单总结(二十一)—— 方法
22. swift简单总结(二十二)—— 下标脚本
23. swift简单总结(二十三)—— 继承
24. swift简单总结(二十四)—— 构造过程
25. swift简单总结(二十五)—— 构造过程
26. swift简单总结(二十六)—— 析构过程
27. swift简单总结(二十七)—— 自动引用计数
28. swift简单总结(二十八)—— 可选链
29. swift简单总结(二十九)—— 类型转换
30.swift简单总结(三十)—— 嵌套类型
31.swift简单总结(三十一)—— 扩展
32.swift简单总结(三十二)—— 协议
泛型
泛型是swift
强大特性中的其中一个,例如:swift
数组和字典类型都是泛型集,你可以创建一个Int
数组,也可创建一个String
数组或者甚至于可以是任何其他swift
的类型数据数组。
下面主要从几点进行讲述:
- 泛型所解决的问题
- 泛型函数
- 类型参数
- 命名类型参数
- 泛型类型
- 类型约束
- 关联类型
-
where
语句
泛型所解决的问题
先看一个例子,标准的非泛型函数,用来交换交换Int
的值。
class JJPracticeVC: UIViewController {
override func viewDidLoad()
{
super.viewDidLoad()
var someInt = 3
var anotherInt = 107
swapTwoInts(a: &someInt, b: &anotherInt)
print("someInt : \(someInt), anotherInt : \(anotherInt)")
}
func swapTwoInts(a : inout Int, b : inout Int){
let temp = a
a = b
b = temp
}
}
下面看输出结果
someInt : 107, anotherInt : 3
swapTwoInts
函数是非常有用的,但是它只能交换Int
的值,如果你想要交换两个Double
和String
值,就不得不写更多的函数了,例如swapTwoDoubles
和swapTwoString
。
但是,在实际应用中,通常需要一个用处更强大并且尽可能的考虑更多的灵活性的单个函数,可以用来交换两个任意类型值,这里泛型就能帮你解决这个问题。
注意:swift
中不同类型的值不能互换,所以a
、b
和temp
必须是同类型的,否则交换会报错。
泛型函数
泛型函数可以工作与任何类型,这里是一个swapTwoInts
函数的泛型版本,用于交换两个值。
class JJPracticeVC: UIViewController {
override func viewDidLoad()
{
super.viewDidLoad()
var someInt = 3
var anotherInt = 107
swapTwoValue(a: &someInt, b: &anotherInt)
print("someInt : \(someInt), anotherInt : \(anotherInt)")
var someString = " hello "
var anotherString = " world "
swapTwoValue(a: &someString, b: &anotherString)
print("someString : \(someString), anotherString : \(anotherString)")
}
func swapTwoValue (a : inout T, b : inout T){
let temp = a
a = b
b = temp
}
}
下面看输出结果
someInt : 107, anotherInt : 3
someString : world , anotherString : hello
上面利用泛型,可以交换任意两个相同类型的值。这里T
是一个占位命名类型,swift
不会查找命名为T
的实际类型。
类型参数
在上面的例子swapTwoValue
中,占位类型T
是一种类型参数的示例,类型参数指定并命名为一个占位类型,并且紧随在函数名后面,使用一对尖括号括起来
。
你可支持多个类型参数,命名在尖括号中,用逗号分开。
命名类型参数
在简答的情况下,泛型函数或泛型类型需要指定一个占位类型,通常用一个单个字母T
命名类型参数,不过,你可以使用任何有效的标识符来作为类型参数名。
如果你使用多个参数定义更复杂的泛型函数或泛型类型,那么使用更多的描述类型参数是非常有用的,例如,swift
字典dictionary类型就有两个类型参数,一个是键,另外一个就是值。
请始终使用大写字母开头的驼峰命名法,例如T
和KeyType
来给类型参数命名,以表明它们是类型的占位符,而非类型值。
泛型类型
在泛型函数中,swift
允许你定义自己的泛型类型,这些自定义类、结构体和枚举作用与任何类型。
下面展示的是泛型集类型,stack
栈。
有栈就有入栈和出栈等操作,如上图展示。
struct IntStruct {
var items = [Int]()
mutating func push(item : Int){
items.append(item)
}
mutating func pop() ->Int{
return items.removeLast()
}
}
上面的结构体就展示的是入栈和出栈等操作。上面的只限于整型值,下面我们对其进行扩展,以适应各种类型。
struct Stack {
var items = [T]()
mutating func push(item : T){
items.append(item)
}
mutating func pop() ->T{
return items.removeLast()
}
}
下面调用一下
class JJPracticeVC: UIViewController {
override func viewDidLoad()
{
super.viewDidLoad()
var stackOfString = Stack()
stackOfString.push(item: "uno")
stackOfString.push(item: "dos")
stackOfString.push(item: "tres")
stackOfString.push(item: "cuatro")
print(stackOfString)
}
}
下面看输出结果
Stack(items: ["uno", "dos", "tres", "cuatro"])
下面看一下入栈示意图。
下面我们在看一下出栈
stackOfString.pop()
看一下输出结果
Stack(items: ["uno", "dos", "tres"])
下面看一下出栈示意图。
类型约束
虽然前面的都是定义任何类型的泛型,但是有时候强制限制类型是很有用的,类型约束指定了一个必须继承自指定类的类型参数,或者遵循一个特定的协议或协议构成。比如:swift
字典的键类型就进行了约束,一定要是可哈希的,键必须遵循哈希协议Hashable
,所有swift
基本类型如Int
、String
、Double
和Bool
都是可哈希的。
1. 类型约束语法
你可以写一个在一个类型参数名后面的类型约束,通过冒号分割,来作为类型参数链的一部分,这种作用于泛型函数的类型约束的基础语法如下所示。
func someFunction (someT : T, someU : U) {
//function body goes here
}
上面语法实例汇总,有类型参数,一个是T
,有一个需要T
必须是SomeClass
子类的类型约束;第二个类型参数U
,有一个需要U
遵循SomeProtcol
协议的类型约束。
2. 类型约束行为
先看一个简单例子。
class JJPracticeVC: UIViewController {
override func viewDidLoad()
{
super.viewDidLoad()
let stringArr = ["cat", "dog", "llama", "parakeet", "terrapin"]
if let foundIndex = findStringIndex(array: stringArr, valueToFind: "llama") {
print("The index of llama is \(foundIndex)")
}
}
func findStringIndex(array : [String], valueToFind : String) -> Int?{
for (index, value) in array.enumerated() {
if value == valueToFind {
return index
}
}
return nil
}
}
下面是输出结果
The index of llama is 2
上面这个实例的功能就是查找数组中指定元素额的下标,这里只是针对字符串而言有效,其实我们还可以扩展到指定类型有效。
func findIndex(array : [T], valueToFind : T) -> Int?{
for (index, value) in array.enumerated() {
if value == valueToFind {
return index
}
}
return nil
}
上面就是扩展后的实例,但是这个不会编译通过,报错。问题出在if value == valueToFind
上面,不是所有的swift
中的类型都可以用等式符==
进行比较,比如,你自己创建一个你自己的类或结构体来表示一个复杂的数据模型,但是swift
没法猜到对于这个类或结构体而言“等于”的意思,正因如此,这部分代码不能保证工作于每个可能的类型T
。
不过,这个可以解决,swift
标准库中定义了一个Equatable
协议,该协议要求任何遵循的类型实现等式符==
和不等式符!=
对任何两个该类型进行比较,所有的swift
标准类型自动支持Equatable
协议。
下面我们改进一下代码。
class JJPracticeVC: UIViewController {
override func viewDidLoad()
{
super.viewDidLoad()
let stringArr = ["cat", "dog", "llama", "parakeet", "terrapin"]
let stringResult = findIndex(array: stringArr, valueToFind: "dog")
print("stringResult \(stringResult)")
let doubleArr = [3.14, 4.34, 1.00, 252.9]
let doubleResult = findIndex(array: doubleArr, valueToFind: 3.14)
print("doubleResult \(doubleResult)")
}
func findIndex(array : [T], valueToFind : T) -> Int?{
for (index, value) in array.enumerated() {
if value == valueToFind {
return index
}
}
return nil
}
}
下面看一下输出结果
stringResult Optional(1)
doubleResult Optional(0)
这里,findIndex
中
意味着“任何T类型都遵循Equatable
协议”。
关联类型 - associatedtype
当定义一个协议时,有时候需要声明一个或多个关联类型作为协议定义的一部分是非常有用的,一个关联类型作为协议的一部分,给定了类型的一个占位名(或别名),作用于关联类型上实际类型在协议被实现前是不需要指定的,关联类型被指定为associatedtype
关键字。
1. 关联类型行为
下面是一个简单例子。
protocol Container {
associatedtype ItemType
mutating func append(item : ItemType)
var count : Int{
get
}
subscript(i : Int) -> ItemType{
get
}
}
Container
协议定义了三个任何容器必须支持的兼容要求
- 必须可能通过
append
方法添加一个新的item
到容器里; - 必须可能通过使用
count
属性获取items
的数量,并返回一个Int
值; - 必须可能通过容器的
Int
索引值下标可以检索到每一个item
。
Container
协议没有指定容器里的item
是如何存储或何种类型是允许的,这个协议只指定三个任何遵循Container
类型所必须支持的功能点,一个遵循的类型在满足这三个条件的情况下也可以提供其他额外的功能。
任何遵循Container
协议的类型必须指定存储在其里面的值类型,必须保证只有正确类型的items
可以加进容器里面,必须明确可以通过其下标返回item
类型。
为此,定义了ItemType
的关联类型,写作associatedtype
,这个协议不会定义ItemType
是什么别名,这个信息将由任何遵循协议的类型来提供。
下面看一个简单例子。
struct IntStack : Container{
var items = [Int]()
mutating func push(item : Int){
items.append(item)
}
mutating func pop() -> Int{
return items.removeLast()
}
//遵循Container协议的实现
typealias ItemType = Int
mutating func append(item: Int) {
self.push(item: item)
}
var count: Int{
return items.count
}
subscript(i : Int) -> Int{
return items[i]
}
}
上面就是将"Int"和ItemType
进行关联,下面我们就看一下泛型在这里的使用。
struct Stack : Container{
var items = [T]()
mutating func push(item : T){
items.append(item)
}
mutating func pop() -> T{
return items.removeLast()
}
//遵循Container协议的实现
mutating func append(item: T) {
self.push(item: item)
}
var count: Int{
return items.count
}
subscript(i : Int) -> T{
return items[i]
}
}
2. 扩展一个存在的类型为一指定关联类型
前面定义了Container
协议,意味着你可以扩展Array
去遵循Container
协议。只需要简单的声明Array
适用于该协议而已。下面定义的就是实现一个空扩展行为。
extension Array : Container{
}
Where语句
对关联类型定义约束是非常有有用的,你可以在参数列表中通过where
语句定义参数的约束,一个where
语句能够使一个关联类型遵循一个特定的协议,以及那个特定的类型参数和关联类型可以是相同的,可以写一个where
语句,紧跟在类型参数列表后面,where
语句后跟一个或者多个针对关联类型的约束,以及一个或多个类型和关联类型间的等价关系。
看下面这个简单例子。
func allItemsMatch(someContainer : C1, anotherContainer : C2) -> Bool{
if someContainer.count != anotherContainer.count {
return false
}
for i in 0...someContainer.count {
if someContainer[i] != anotherContainer[i] {
return false
}
}
return true
}
这个函数的作用就是检查两个Container
实例是否包含相同顺序的相同元素。这个函数的类型参数紧随在两个类型参数需求的后面。
-
C1
必须遵循Container
协议 -
C2
必须遵循Container
协议 -
C1
的ItemType
同样也是C2
的ItemType
-
C1
的ItemType
必须遵守Equatable
协议。
后记
未完,待续~~~