目录
[TOC]
1. 概述
1.1 简介
-
Swift 语言由苹果公司在 2014 年推出,用来撰写 OS X 和 iOS 应用程序
-
一年一个版本,Swift已经发布到3.0
-
和ObjC只有语法的差异,花两三天时间的练习基本可以熟悉基本语法,其他高级的随着开发过程中慢慢来
-
例子:
// ObjC 创建颜色 [UIColor whiteColor]; // Swift 创建颜色 UIColor.redColor() UIColor.red
1.2 Swift之父
- 2010 年 7 月,苹果开发者工具部门总监
Chris Lattner
开始着手 Swift 编程语言的设计工作 - 用一年时间,完成基本架构,Swift 大约历经 4 年的开发期,2014 年 6 月发布
- <u>克里斯·拉特纳</u> 何许人?
- 苹果公司『开发者工具』部门的主管
- Xcode、Instruments工具都是由他领导开发的
- Swift的大部分基础架构均由他1人完成
1.3 特点
- 从它的语法中能看到Objective-C、JavaScript、C#、Python等语言的影子
- 速度快,效率高
- 可与Objective-C混合使用(相互调用)
- 提供了类似 Java 的名字空间(namespace)、泛型(generic)、运算符重载(operator overloading)
1.4 重要性
- 苹果在大力推广Swift,目前国外很多项目都用Swift重写了,在编程语言排行榜中Swift已经超过了ObjC
- 国内完全使用Swift的公司较少,但也都在少量使用或项目中局部引入
- 迁移代价高(人员需要学习,第三方框架)
- Swift语法不向下兼容且不够稳定,每一次版本更新都需要改一大堆语言更新引发的bug,不过Xcode提供了转换工具,再部分手动改动即可
- 如何学习
- 熟悉基本语法,切勿想要一次性将语法全部记住,效率太低
- 大量练习,在联系中将语法巩固,逐步接受Swift的高级语法
1.5 学习资源
2. 初识Swift
2.1 HelloSwift
- 注释
- 单行
- 多行
- 导入
- 打印
- 语句结束没有分号
- main方法
- 标记
- //MARK:
- //TODO:
- //FIXME:
2.2 Playground
2.2.1 简介
Playground是苹果从Xcde6开始在Xcode添加的新功能。使用Xcode创建工程编写和运行程序,目的是为了使最终的程序编译和发布,而使用Playground的目的是为了学习、测试算法、验证想法和可视化地看到运行结果。
- 代码区域
- 结果区域
- 时间轴区域
Xcode 从6.0开始加入了Playground代码测试,可以实时查看代码执行结果。
2.2.2 常量和变量
-
认识常量变量
-
类型标注
-
类型推断
-
打印格式化
-
展示image
2.3 命名
2.3.1 命名规定
命名规定:
- 你可以用任何你喜欢的字符作为常量和变量名,包括 Unicode 字符
- 常量与变量名不能包含数学符号,箭头,保留的(或者非法的)Unicode 码位,连线与制表符。也不能以数字开 头,但是可以在常量与变量名的其他地方包含数字。
2.3.2 命名规范
驼峰命名法:
驼峰命名(Camel-Case),又称骆驼命名法,是指混合使用大小写字母来名字。驼峰命名又分为:小驼峰法和大驼峰法。
- 小驼峰法是第一个单词是全部小写,后面的单词首字母大写,如:myRoomCount;
- 大驼峰法是第一个单词的首字母也大写,如:ClassRoom。
具体规则:
- 对类、结构体、枚举和协议等类型命名,应该采用大驼峰法,如SplitViewController。
- 文件名,采用大驼峰法,如BlockOperation.swift。
- 扩展文件,有的时候扩展是定义在一个独立的文件中的,它的命名是“原始类型名+扩展名”作为扩展文件名,如NSOperation+Operations.swift。
- 变量和属性,采用应该采用小驼峰法,如studentNumber。
- 常量,采用大驼峰法,如MaxStudentNumber。
- 枚举成员,与常量类似,采用大驼峰法,如ExecutionFailed。
- 函数和方法,采用应该采用小驼峰法,如balanceAccount、isButtonPressed等。
3. 基本数据类型
3.1 基本数据类型
- 整型
- 有符
- 无符
- 浮点型
- Double (默认的,64位)
- Float (32位)
- Bool类型
- true
- false
3.2 数值型类型转换
- 整型和浮点型之间的转换
4. 运算符
4.1 算术运算符
-
+ - * / % ++ -- ++ 和 -- 在Swift3.0中已经废弃 要使用+=1 -=1
4.2 赋值运算符
-
= += -= /= *= %= // 不允许连续赋值, Swift中的整个表达式是没有值的.
4.3 关系运算符
-
> < >= <= == != ?:
4.4 逻辑运算符
-
! && ||
4.5 Swift新增--区间运算符
-
... ..< /* 闭区间: 包含区间内所有值 a...b 例如: 1...5 半闭区间: 包含头不包含尾 a..<b 例如: 1..<5 注意: 1.Swift刚出来时的写法是 a..b 2.区间只能用于整数, 写小数会有问题 */
4.6 Swift新增--溢出运算符
-
&+ &- &* &/ &%
5. 控制流
5.1 逻辑分支
5.1.1 if语句
-
if语句注意事项:
- if后的圆括号可以省略
- 只能以Bool作为条件判断
- 如果只有一条指令if后面的大括号不可以省略
-
代码:
if 条件表达式 { 语句 } if 条件表达式 { 语句 } else{ 语句 }
5.1.2 Switch语句
-
Switch语句注意事项
- 不写break依旧跳出匹配,不会贯穿
- 支持一个case,匹配多个模式,模式之间逗号隔开
- case中使用变量可以不写大括号,但是末尾必须要加default
- 匹配区间
- 元组匹配,值绑定、where条件匹配
-
代码:
// 匹配区间 let c:Int=88 switch c{ case 1...60: println("1-60") case 61...90: println("61-90") case 91...100: println("91-100") default: println("1>c>100") } // 匹配元组,值绑定,where条件匹配 var d=(x:900,y:0) switch d{ case (0,0): print("d in (0,0)") case (_,0): //忽略x值匹配 print("d in y") //值绑定 case (0,let y): print("d in x,y=\(y)") //注意这里有可能和第一、二、三个条件重合,但是Swift允许多个case匹配同一个条件,但是只会执行第一个匹配 case (-100...100,-100...100): println("x in(0-100),y in (0-100)") //where条件匹配,注意这里的写法等同于:(let x,let y) where x==y case let (x,y) where x==y: println("x=y=\(x)") case let (x, y): println("x=\(x),y=\(y)") }
5.2 循环使用
5.2.1 for循环
-
C格式for循环 (在Swift3.0中被干掉)
-
for in循环 区间
5.2.2 while循环
- while
- dowhile Swift2.0被Repeat While替换
var i = 20;
while i >= 10 {
i -= 1
print("while \(i)")
}
repeat{
i += 1
print("do while \(i)")
} while i < 0
5.2.3 break-continue
- break: 跳出循环, 无论循环保持条件是否还为真都不会再执行循环
- continue: 跳出本次循环, 如果循环保持条件还为真还会继续执行循环
6. Foundation
字符串、枚举、结构体、集合等都是值类型
6.1 字符串
-
不可变字符串
-
可变字符串
-
获取字符串长度
-
字符串拼接
let f = 10.987 var str = "\(f)" let str2 = "345" str += str2 print("str = \(str)")
-
判断字符串是否相等
-
判断前后缀
-
大小写
-
转换为基本数据类型 注意:返回可选类型
6.2 数组
-
声明数组时必须确定其类型
// 数组中的元素同一类型 let arr1 = [1, 2, 3] let arr2: Array<Int> = [1, 2, 3] let arr3: [Int] = [1, 2, 3] //let arr4: Int[] = [1, 2, 3] 早期写法 // 数组中的元素不同类型 let arr1 = [1, 2, 3] as [Any] let arr2: Array<Any> = [1, 2, 3] let arr3: [Any] = [1, 2, 3] //let arr4: Int[] = [1, 2, 3] 早期写法
-
可变数组和不可变数组
let vs var
-
获取数组元素的个数
-
判断数组是否为空
-
获取数组中的元素
-
数组添加元素
- 方法1 += 后面跟数组类型
- 方法2 append
-
数组插入元素
-
数组替换元素
-
数组删除元素
-
数组遍历
6.3 集合 Set
- 集合和数组的区别:数组中的元素是有序的,集合中的元素是无序的
6.4 字典
- 创建
- 获取字典中的值
- 添加键值对
- 修改字典中的值
- 字面量
- 方法
- 删除字典中的键值对
- 字典的遍历
6.5 元组
-
元组的作用
- 方便的组织一个简单的临时性的数据结构使用
- 元组的添加可以方便的让函数返回多个值
-
创建元组
- 声明元组,没有类型,没有元素名称; 取值时通过下标来取
- 声明元组,没有类型,指定元素名称;取值时可以通过下标和名称来取
- 声明元组,指定类型,没有元素名称;取值时通过下标来取 注意不能既指定类型又指定元素名称
-
一次性赋值给多个变量
-
使用通配符
_
忽略元组中的某个值
6.6 可选类型
ObjC中如果一个变量没有值
- 如果是对象类型则是初始化为nil
- 基本数据类型则是对应0的默认值(全局变量)或者一个不确定值(局部变量)
Swift是强类型语言,对这些要求比较严格,如果初始化时没有进行赋值,Swift不会默认设置初始值
6.6.1 可选类型
var optionalString: String?
var optionalString: Optional<String>
注意:
String?
和String
并不是一种类型,不能直接进行运算,如果要运算,需要强制解包- 可选类型的本质是此类型的内部分为
Some
和None
两部分, 有值 则是存到Some
,没值则为None
- 简单来说,可选类型有两种状态,1、有值 2、没有值,没有值就是nil
6.6.2 强制解包
强制解包即提取可选类型的值
- 在进行运算时,在可选类型变量后面加感叹号表示强制解包,取出该变量的值
- 如果变量没有值,强制解包会产生运行错误,造成崩溃
6.6.3 可选绑定
- 为了更安全的解析可选类型的值,一般情况下使用可选绑定
- 判断的同时如果有值则将值赋值给一个临时变量或常量,否则不进入此条件语句,这个过程称之为“可选绑定”
var optValue: Int?
optValue = 9
// if 可选绑定
if let result3 = optValue
{
print("optValue有值 = \(result3)")
}
else
{
print("optValue没有值")
}
// while可选绑定
while let result3 = optValue
{
statement
}
6.6.4 隐式可选类型
var implicitlyUnwrappedString: String!
var implicitlyUnwrappedString: ImplicitlyUnwrappedOptional<String>
- 当可选类型被第一次赋值之后就可以确定之后一直有值的时候,此时可以使用隐式可选类型。通常用于构造构成
- 一个隐式可选类型其实就是一个普通的可选类型,但是可以被当做非可选类型来使用,并不需要每次都使用 解析来获取可选值,即声明时可以不赋值,在初始化方法中赋值,以后使用时还不用像可选类型那样解包
- 注意
- 如果在变量没有值时进行取值,会触发运行错误,和强制解包一个没有值的可选类型是一样的
- 在Swift3.0中,如果直接将 隐式可选类型 赋值给一个常量或变量,得到的是 可选类型
6.6.5 可选链
-
可选链式调用是一种可以在当前值可能为 nil 的可选值上请求和调用属性、方法及下标的方法。
-
如果可选值有值,那么调用就会成功;如果可选值是 nil ,那么调用将返回 nil 。
-
多个调用可以连接在一起形成一个调用 链,如果其中任何一个节点为 nil ,整个调用链都会失败,即返回 nil 。
-
实例:人(People)有一辆车(Car),车上有轮子(Wheel),轮子有型号(Model)
class People{ var car: Car? } class Car { var wheel: Wheel? func running(){ print("跑起来") } } class Wheel{ var model = "325" } let p = People() let c = Car() let w = Wheel() p.car = c c.wheel = w let model = p.car?.wheel?.model p.car?.wheel = Wheel() p.car?.running()
6.7 类型转化
-
as
将实例转化成某一种数据类型 -
代码:
let array: [Any] = [12, 1, 2] let obj = array[1] // 转化为可选类型 let age = obj as? Int // 转化为具体的类型,如果不是该类型,运行错误,程序崩溃 let age1 = obj as! Int
7. 函数
7.1 函数定义
-
函数: 完成某个特定任务的代码块, 给代码起一个合适的名称称之为函数名称. 以后需要执行代码块只需要利用函数名称调用即可, 好比每个人都有一个名字, 叫名字就能找到对应的人
-
函数格式:
//func 函数名称(参数名:参数类型, 参数名:参数类型...) -> 函数返回值 {函数实现部分} func methodName(p1: p1Type, p2: p2Type, ...) -> returnType { // 函数实现部分 }
7.2 函数的参数
-
函数参数名分为局部参数名和外部参数名
-
代码:
func method(p1 param1: Int, p2 param2: Int) -> Int { return param1 + param2; } let _ = method(p1: 1, p2: 2)
-
-
如果参数有默认值
-
代码:
func method(p1 param1: Int, p2 param2: Int = 2) -> Int { return param1 + param2 } // 调用时可以使用默认值 let _ = method(p1: 1) // 也可以不使用默认值 let _ = method(p1: 1, p2: 4)
-
-
在局部参数名前什么都不写,表示局部参数名和外部参数名相同(Swift2.0以前是局部参数名前加#符号表示局部和外部相同)
-
代码:
func method(param1: Int, param2: Int) -> Int { return param1 + param2; } let _ = method(param1: 1, param2: 2)
-
-
使用 _ 取消外部参数
-
代码:
// 关键字 表示忽略返回值 Swift3.0特性 @discardableResult func method(_ param1: Int, p2 param2: Int = 2) -> Int { return param1 + param2 } method(10, p2: 10)
-
-
可变参数
-
代码:
func joinStr(seperator:Character=",",strings:String...)->String { var result:String="" for i in 0..<strings.count { result.append(seperator) result += strings[i] } } joinStr(strings: "hello","world","!") //结果:"hello world !"
-
-
输入输出参数:通过输入输出参数可以在函数内部修改函数外部的变量(注意调用时不能是常量或字面量)
-
代码:
//例1 // swift3.0以前 func increase(inout a:Int) { a += 1 } // swift3.0更新 上面的代码会报错,可改为 func increase( a:inout Int) { a += 1 } var value = 1 increase(&value) // 结果 value = 2 // 例2 func swapValue(a:inout Int ,b:inout Int){ a=a+b b=a-b a=a-b } var a=1,b=2 //调用时参数加上“&”符号 swap(&a, &b) //结果:"a=2,b=1" print("a=\(a),b=\(b)")
-
7.3 函数类型
函数类型是由函数的参数类型和返回值类型组成的
- 函数类型定义函数变量和常量
- 函数类型做参数
- 函数类型的返回值
8. 类
8.1 初识类
- 作为一门面向对象语言,类当然是Swift中的一等类型
- 声明一个类
- Swift中的类可以不继承其他任何基类
- 定义属性
- 定义方法
- 方法定义
- 实例方法
- 方法和函数的区别
- ObjC中 函数只能是C语言,方法时ObjC的
- Swift中 方法和函数并无明确区别,方法除了在类中使用还可以在结构体和枚举中使用
- 方法定义
- 定义构造方法和默认无参构造方法
- 析构方法,没有括号和参数且不支持手动调用
- 判断两个对象是否相等
- 类是引用类型,枚举和结构体是值类型
8.2 属性
- 存储属性(用于类、结构体)
- 定义:简单来说,一个存储属性就是在特定类或结构体实例里的用于保存至的一个常量或变量
- 计算属性(用于类、结构体、枚举)
- 定义:类、结构体和枚举可以定义计算属性。计算属性不直接存储值,而是提供一个 getter 和一个可 选的 setter,来间接获取和设置其他属性或变量的值。
- 计算属性的格式以及使用
- 字典转model
- 设置set方法的newValue变量
- 只读计算属性
- 属性观察器
- 定义:属性观察器监控和响应属性值的变化,每次属性被设置值的时候都会调用属性观察器,即使新值和当前值相同的时候也不例外
- 场景
- 自己的存储属性添加属性观察器,自己的计算属性则没必要
- 类型属性
- 为类或结构体添加属性,使用static关键字 (结构体使用static)
8.3 方法
Swift 默认仅给方法的第一个参数名称一个局部参数名称;默认同时给第二个和后续的参数名称为全局参数名称。
- 实例方法
- 类型方法
- 指定构造方法
- 便利构造方法 调用指定构造方法,提供默认值来简化构造方法的实现
- 析构方法
8.4 下标subscript
subscripts(下标): 访问对象中数据的快捷方式
所谓下标脚本语法就是能够通过, 实例[索引值]来访问实例中的数据
类似于以前我们访问数字和字典, 其实Swift中的数组和字典就是一个结构体
- 让一个普通的类实现可以类似于数组或字典那样的取值赋值
8.5 继承
继承是面向对象最显著的一个特性, 继承是从已经有的类中派生出新的类
新的类能够继承已有类的属性和方法, 并能扩展新的能力
术语: 基类(父类, 超类), 派生类(子类, 继承类)
- super关键字
- 方法重写
- 重写属性
- 无论是存储属性还是计算属性, 都只能重写为计算属性
- 利用final关键字防止重写
- final关键字既可以修饰属性, 也可以修饰方法, 并且还可以修饰类
- 被final关键字修饰的属性和方法不能被重写
- 被final关键字修饰的类不能被继承
9. 枚举和结构体
9.1 枚举类型
-
Swift中的枚举比OC中的枚举强大, 因为Swift中的枚举可以像类和结构体一样增加属性和方法
-
声明格式
enum <#name#> { case <#case#> } enum EnumName { case 枚举值1, 枚举值2, 枚举值3 } // 如果声明时 指定了类型 后面赋值时可以省略 var e: EnumName = .枚举值 // 但是如果声明时没有指定类型,在赋值时必须指定类型 var e = EnumName.枚举值
-
而Swift中的枚举默认是没有原始值的, 但是可以在定义时告诉系统让枚举有原始值,递增
enum Method: 枚举值原始值类型{ case 枚举值 }
-
Swift中的枚举除了可以指定整型以外还可以指定其它类型,但是如果指定其他类型,必须给所有枚举值赋值,因为不能递增
enum NSHTTPMethod: String { case Get = "GET", Post = "POST" } // 获取枚举的原始值 print(NSHTTPMethod.Get.rawValue) // 通过原始值创建枚举值 let e = NSHTTPMethod(rawValue: "GET")
9.2 结构体
-
结构体是用于封装不同或相同类型的数据的, Swift中的结构体是一类类型, 可以定义属性和方法(甚至构造方法和析构方法等)
-
声明格式
struct 结构体名称 { 结构体属性和方法 }
-
如果结构体的属性有默认值,可以使用系统默认提供的构造方法构建一个,但是如果没有默认值,构造方法必须初始化属性且属性和结构体中成员顺序一致,系统自动生成一个全员逐一构造函数
-
注意如果自定义了构造方法,则系统不再自动生成默认构造函数
struct Position { var x: Float = 0.0 var y: Float = 0.0 } var p = Position() print("x=\(p.x), y=\(p.y)") var p2 = Position(x:10, y:10) print("x=\(p2.x), y=\(p2.y)")
-
Swift中的机构体不但有属性,还可以定义方法
struct Position { var x: Float = 0.0 var y: Float = 0.0 func getX() -> Float { return x } }
9.3 枚举和结构体都是值类型
10. 闭包
闭包和ObjC中的block基本一样
10.1 闭包
-
闭包的声明和调用
闭包的写法: /* 类型: (参数类型1, 参数类型2) -> (返回值类型) 值: { (参数1: 参数类型1) -> (返回值类型) in //代码 } */ let block: (Int, Int) ->(Int) = {(a, b) -> (Int) in return a + b } let _ = block(10, 20) let block2: () -> () = { } block2()
-
闭包表达式优化
- 类型优化,声明时已确定闭包参数类型,闭包体内参数不需要写类型
- 返回值优化,声明时已确定闭包返回值,闭包体内返回值不需要写类型,去掉
-> ()
- 参数优化,Swift可以使用$索引的方式来访问闭包的参数, 默认从0开始
- 如果只有一条语句,可以省略return
-
闭包作为函数的参数
-
self.callBlock(a, b, {(num1: Int, num2: Int) -> Int in $0 - $1 })
-
-
尾随闭包
-
// 闭包是函数的最后一个参数,则可以将闭包写在括号后面 self.callBlock(){(a:Int) -> () in print("打印:\(a)") } // 如果函数只有一个参数,并且这个参数是闭包,那么()可以不写 self.callBlock{ print("打印:\(a)") }
-
10.2 闭包捕获值
闭包之所以称之为“闭包”就是因为其可以捕获一定作用域内的常量或者变量进而闭合并包裹着。
func add()->()->Int{
var total=0
var step=1
func fn()->Int{
total+=step
return total
}
return fn
}
//fn捕获了total和step,尽管下面的add()执行完后total和step被释放,但是由于fn捕获了二者的副本,所以fn会随着两个变量的副本一起被存储
var a=add()
a() //结果:1
a() //结果:2,说明a中保存了total的副本(否则结果会是1)
var b=add()
b() //结果:1 ,说明a和b单独保存了total的副本(否则结果会是3)
var c=b
c() //结果:2,说明闭包是引用类型,换句话说函数是引用类型(否则结果会是1)
10.3 闭包循环引用
-
Swift中闭包的循环引用与ObjC中block的循环引用原理是一样的
-
循环引用的产生
class Tools: NSObject{ var finishedCallBack:((_ jsonStr: String) -> ())? func requestData(finishedCallBack:(_ jsonStr: String) -> ()) { self.finishedCallBack = finishedCallBack } }
-
解决循环引用一
-
使用weak,对当前对象弱引用
weak var weakSelf = self tool.requestData{ (jsonStr: String) -> () weakSelf!.view.backgroundColor = UICorlor.redColor() }
-
-
解决循环二:
-
tool.requestData{ [weak self](jsonStr: String) -> () self!.view.backgroundColor = UICorlor.redColor() }
-
-
解决循环三:
-
使用关键字unowned
-
tool.requestData{ [unowned self](jsonStr: String) -> () self!.view.backgroundColor = UICorlor.redColor() }
-
11. 协议
-
协议的声明
protocol <#name#> { <#requirements#> } protocol XSQTestDelegate { // 协议方法 默认都是必须实现的 func ok() } // 在Swift3.0之前如果要定义协议中可选方法,只需要给协议加上@objc之后方法使用optional修饰就可以了,但是Swift3.0中除了协议需要@objc修饰,可选方法也必须使用@objc来修饰。 @objc protocol XSQAlertViewDelegate { // 协议方法 默认都是必须实现的 @objc optional func ok() }
-
协议使用
@objc protocol XSQAlertViewDelegate { // 协议方法 默认都是必须实现的 func test() @objc optional func ok() } //要使类遵循某个协议,需要在类型名称后加上协议名称,中间以冒号:分隔,作为类型定义的一部分。遵循多个协议时,各协议之间用逗号,分隔。 class People: XSQAlertViewDelegate, xxxxDelegate { func test(){ } func ok(){ } } // 如果类在遵循协议的同时拥有父类,应该将父类名放在协议名之前,以逗号分隔。 class People: NSObject, XSQAlertViewDelegate, xxxxDelegate { func test(){ } func ok(){ } }
12. 扩展
-
Swift中的扩展就类似于ObjC中的分类(事实上在其他高级语言中更多的称之为扩展而非分类),但是它要比分类强大的多,它不仅可以扩展类还可以扩展协议、枚举、结构体,另外扩展也不局限于扩展方法(实例方法或者类型方法),还可以扩展便利构造方法、计算属性、下标脚本
-
格式:
extension People{ func eat() { } }
13. Swift和ObjC互相调用
14. 内存管理
Swift内存管理:
管理引用类型的内存, 不会管理值类型, 值类型不需要管理
内存管理原则: 当没有任何强引用指向对象, 系统会自动销毁对象
(默认情况下所有的引用都是强引用)
- 循环引用