Swift介绍
1.Swift由来:
2010 年 7 月,苹果 开发者工具部门总监 Chris Lattner 开始着手 Swift 编程语言的设计工作(用一年时间,完成基本架构)
Swift 语言由苹果公司在 2014 年推出,用来撰写 OS X 和 iOS 应用程序,2016年9月, 正式发布了Swift3.0 (上一个版本是2.2,没有固化API)
2.Swift 和 C/OC之间的关系?
Swift 结合了 C 和 Objective-C 的优点并且不受 C 兼容性的限制。Swift 采用安全的编程模式并添加了很多新特性。
3.Swift特点:
①从它的语法中能看到Objective-C、JavaScript、C#、Python等语言的影子
②语法简单、代码简洁、使用方便, 类型安全
③可与Objective-C混合使用(相互调用)
④提供了类似 Java 的命名空间(namespace)、泛型(generic)、运算符重载(operator overloading)
总结: 功能大, 性能高, 使用简洁, 代码安全
4.Swift的重要性:
①从Swift的几个重大版本迭代, 苹果目前在大力推广Swift
②斯坦福大学的公开课目前也是使用Swift在授课.因为以后Swift确实是往后发展的趋势
③现在很多公司, 已经开始使用Swift进行重构项目
注意:目前而言, 市场上开发APP, 依旧是以OC为主; 所以OC也不能扔;Swift 它采用了 Objective-C 的命名参数以及动态对象模型,可以无缝对接到现有的 Cocoa 框架,并且可以兼容 Objective-C 代码。
5.Swift可以做哪些事情:
①iOS App
②Mac App
③服务器端
④安卓端
Swift语法
须知:
关于分号,swift中, 一行语句结束, 可以不加分号,添加的话, 也不会报错,但是如果一行有多条语句, 还是需要使用分号进行分割。
1.常量&变量
1)定义格式 (注意: [] 内代表可以省略)
let 常量名称 [:常量类型] = 值
var 变量名称 [:变量类型] = 值
2)注意事项:
- 必须指明是常量还是变量:常量: let 变量: var
- 变量名称 可以是汉字, 或者Unicode字符
- 可以省略类型,swift中有个类型推导机制, 会自动根据值来判断是什么类型(常用option键 查看 变量或常量的类型)
- 值必须和类型相匹配, 否则报错
- Swift是类型安全的语言, 必须是相同类型的参数, 才能进行运算
3) 开发选择:
建议优先选择常量, 如果需要修改, 选择变量
2.基本数据类型&类型转换&类型推导
注意:Swift中的数据类型也有:整型/浮点型/对象类型/结构体类型等等
1)整形
OC:
有符号: signed 例如: NSInteger (跟平台, CPU相关)
无符号:unsigned 例如:NSUInteger (跟平台, CPU相关)
Swift:
有符号: 例如:Int : 和平台相关(默认,相当于OC的NSInteger)
无符号: 例如:UInt : 和平台相关(常用,相当于OC的NSUInteger)(默认)
2)浮点型
OC与Swift均包含:float 和 double
3)布尔类型
OC:
C语言和OC并没有真正的Bool类型,C语言的Bool类型非0即真,
OC语言的Bool类型是:
①定义一个布尔类型 typedef signed char BOOL;
②条件判断中:OC中if条件可以是任何 整数(非0即真)
存在的问题是:可能将判断写错, 写成了赋值 if(isOpen = 2) ,
在开发中为了避免这个问题有经验的程序员会这样写 if(2 == isOpen)来避免这个问题.
Swift:
引入了真正的Bool类型:true/false
Swift中if的条件只能是一个Bool的值或者是返回值是Bool类型的表达式(==/!=/>/<等等)
注意事项:
由于Swift是类型安全的语言:
①Swift必须明确数据类型(1. 直接指明数据类型 2. "类型推导机制"自动推导)
②如果取值错误会直接报错, 而OC不会
③Swift会进行溢出检查, 而OC不会
④Swift没有隐式类型转换, 不允许不同类型的数据类型进行运算(需要进行强制转换)
3.Swift基本运算:
OC: 有隐式转换, 所以允许不同类型的数据进行运算
Swift: 没有隐式转换, 必须指明类型后才能运算!!
1)算术运算符: + - * / % (注意在swift3.0中, ++ --运算已经被取消 使用 += , -=代替)
2)关系运算符: > < >= <= == !=
3)赋值运算: = += -= /= *= %=
4)逻辑运算符: ! && || ( 基本用法和OC一样)
注意:① Swift中的逻辑运算符只能操作Bool类型数据, 而OC可以操作整形(非0即真)
②溢出检查(会对显式/隐式的数值进行编译检查)
4.逻辑分支(if else if的使用)
- OC:
①if 后面条件必须加()
②if 后面条件, 非0即真 (容易引发一些没有必要的问题)
③如果只有一条指令if后面的大括号可以省略 - Swift:
①if 后面条件不加()
②if 后面条件, 必须是明确的Bool类型
③即使只有一条指令if后面的大括号不可以省略
④if else if的使用:类似OC
⑤三目运算符:OC和Swift基本一致
5.逻辑分支(guard的使用)
Swift特有的:
①guard是Swift2.0新增的语法,它与if语句非常类似,它设计的目的是提高程序的可读性
②guard 必须使用在函数里面
③guard语句必须带有else语句,它的语法如下:
当条件表达式为true时候跳过else语句中的内容,执行语句组内容
条件表达式为false时候执行else语句中的内容
(跳转语句一般是return、break、continue和throw)
6.逻辑分支(switch的基本使用)
- OC:
①switch 后面条件必须加 ( )
②case后面只能跟一个条件
③case会有穿透效果
④可以不写default,default位置可以随便放
⑤在case中定义变量需要加大括号, 否则作用域混乱
⑥不能判断对象或者浮点类型, 只能判断整数 - Swift:
与OC以上条件完全相反
注意:如果想要穿透效果 需要在case 之后使用 fallthrough,默认一个fallthrough只会穿透一层
7.逻辑分支(switch的特殊用法)
Swift新特性:
①区间匹配 比如: case 0…9:
②元组匹配 比如: case (0…10,10…20): 或者 case(_ , 0):等等
③值绑定 比如: case(var x,10): //x有外界传值进来
④根据条件绑定 比如: case var(x, y) where x > y: //只有满足条件才会执行
区间概念解释:通常描述的是数字区间
①半开半闭区间 例如: 0到100(不包括100) => 0..<100
②闭区间 例如: 0到100(包括100) => 0…100
swift3.0: 可以包含小数 例如:let qujian = 0.1...9.9
8.for循环的使用
传统写法:
for var i = 0; i < 10; i ++ { }
区间写法:
for i in 0..<10 { }
或者
for _ in 0..<10 { }
9.while&dowhile循环
总结:
①while 后面的 () 可以省略
②后面的条件没有'非0即真’只能是True或false
③使用repeat关键字来代替了do
例如:
1.while循环
var a = 10
while a > 0 { print(a) }
2.do-while循环
var m = 10
repeat { m -= 1
print(m)
}while(m > 0)
10.字符串的基本使用
1)OC和Swift中字符串的区别:
① 在OC中字符串类型时NSString,在Swift中字符串类型是String
② OC中字符串@"",Swift中字符串""
2)使用 `String` 的原因:
①`String` 是一个结构体,性能更高,而`NSString` 是一个 `OC` 对象,性能略差
②`String` 支持直接遍历
3)字符串的定义:
定义不可变字符串 和定义可变字符串
4)字符串的使用:
①获取字符串的长度
②遍历字符串
③字符串拼接 (字符串和字符串的拼接,字符串和其他数据类型的拼接)
④字符串的格式化 (比如输出时间:03:04)
11.字符串的截取
<1> Swift中提供了特殊的截取方式 :
1)从开始位置截取
let startIndex = url.startIndex
url.substring(from: startIndex)
2)截取至结束位置
let endIndex = url.index(startIndex,offsetBy: 7)
url.substring(to: endIndex)
3)截取中间的字符串
let range = Range(start: str.startIndex.advancedBy(4),end:str.endIndex.advancedBy(-4))
let middle = str.substringWithRange(range)
<2> 字符串替换:
str.replaceSubrange(str.startIndex...str.index(str.startIndex, offsetBy: 2),with: "1234")
<3>简单的方式是将String转成NSString来使用:
`Swift` 提供了 `String` 和 `NSString` 之间的无缝转换 ,在标识符后加as NSString即可
12.数组
1.数组的基本使用:
数组的定义:
不可变数组 let
可变数组 var
数组元素的基本操作: 增删改查
2.数组的其他操作:
①求个数
个数 :count
容量:capacity 一般都是2的倍数(特例: 刚初始化时, 容量大小, 和真实的容量大小一致)
例如:var array = [1, 2, 3]
array.capacity == array.count // 3
②数组遍历
1)一般场景遍历:
for i in 0..<array.count (Swift3.0过期)
2)forin方式:
for item in array
3)设置遍历的区间:
for item in array[0..<2]
4)遍历数组的同时获取下标值
for (index, name) in names.enumerate()
③数组合并
注意:只有相同类型的数组才能合并 ([Any]则什么都可以保存)
不建议一个数组中存放多种类型的数据
13.字典的使用
1.字典的概念:
字典是由两部分集合构成的,一个是键(key)集合,一个是值(value)集合
注意:
①字典允许按照某个键来访问元素
②键集合是不能有重复元素的,而值集合是可以重复的,键和值是成对出现的
③Swift字典类型是Dictionary,也是一个泛型集合
2.字典的初始化:
须知: Swift中的可变和不可变字典( let修饰不可变字典,var修饰可变字典)
声明一个Dictionary类型的时候可以使用下面的语句之一(或者利用自动推倒字典类型):
var dict1:Dictionary<Int, String>
var dict2:[Int: String]
注意:swift中任意值,通常不使用NSObject,使用Any
=> var dict:Dictionary<String, Any>
3.字典元素的基本操作
①增
dic[key] = value 或者 updateValue
注意: 两种方式几乎一致: 一旦发现没有找到对应的key, 都会新增一个键值对
②删
removeValueForKey 或者 removeAll
③改
dic[key] = value 或者 updateValue
④查
dic[key]
4.字典的基本操作
字典的遍历:
遍历字典中所有的值
遍历字典中所有的键
遍历所有的键值对
字典的合并:
字典不可以直接相加合并
=> 合并方案:
for (key, value) in dict1 {
dict2[key] = value
}
14.元组的使用
1.元组的介绍
须知:元组是Swift中特有的,OC中并没有相关类型
①它是一种数据结构,在数学中应用广泛
②类似于数组或者字典,可以用于定义一组数据,组成元组类型的数据可以称为“元素”
例如:使用元组描述一个人的信息 => ("1001", "张三", 30, 90.1)
2.元组的简单使用:
1)数组写法: let array = [404, "Not Found"]
2)元组写法:
方式一:
let error = (404, "NotFound")
print(error.0)
print(error.1)
方式二:
let error = (errorCode: 404,errorInfo: "Not Found")
print(error.errorCode)
print(error.errorInfo)
方式三:
let (errorCode,errorIno) = (404, "Not Found")
print(errorCode)
print(errorIno)
3)元组的应用场景: 参数 / 返回值
15.可选类型的使用
1.可选类型的定义: 一个变量, 要么有值, 要么没有值
2.可选类型产生的原因:
比如: OC中, 基本数据类型, 如果不赋值, 都会存在默认值; 所以不会产生"没有值的情况”
但是: 没有值和有默认值, 是完全不同的两个概念
所以在Swift中引入了"可选类型的概念”: 区分了"有值" 和 "没有值" 的两种状态
3. 定义可选类型
最基本的写法: let name : Optional<String> = nil
语法糖(常用): let name : String? = nil
4. 可选类型的使用
var string : Optional<String> = nil
string = "Hello world” //可选类型赋值
print(string!)
注意:
如果可选类型为nil,强制取出其中的值(解包),会出错
正确写法如下:
方式一:
for string != nil{
print(string!)
}
方式二:可选绑定
if let str = string {
print(str)
}
方式三:guard守护
func test(optionSte: option?){
guard let str = string else{
return
}
print(str)
}
5.真实应用场景:
1. 把字符串转成int类型
2. 根据字符串创建NSURL对象
NSURL(string: "www.520it.com”)
3. 图片的创建
4. 函数的处理
16.类型转换
常见的类型转化符号:
is : 用于判断一个实例是否是某一种类型
as : 将实例转成某一种类型 (比如子类->父类转换)
as? 将某个类型转成可选类型,通过判断可选类型是否有值,来决定是否转化成功了
as! 将某个类型转成具体的类型,但是注意:如果不是该类型,那么程序会崩溃
17.空合运算符
用法: 可选类型的变量 ?? "为空时的代替值” (类似于使用三目运算符判断一个变量是否为空)
意思: 如果可选类型的变量值为nil, 则使用设置的代替值, 如果有值, 直接解包使用
18.函数的基本使用
1)函数的介绍 : 函数相当于OC中的方法
2)swift 函数的格式: func 函数名(参数列表) -> 返回值类型 { 代码块 return 返回值 }
注意: ①func是关键字,多个参数列表之间可以用逗号(,)分隔,也可以没有参数
②使用箭头“->”指向返回值类型
③如果函数没有返回值,返回值为Void. 并且“-> 返回值类型”部分可以省略
3)常见函数类型:
①无参数无返回值
func about() -> Void { print("iphone6s plus") }
简便写法:如果没用返回值,Void可以写成() 或者返回值直接不写
=> func about() { print("iphone6s plus") }
②有参数无返回值
func callPhone(phoneNum : String) { print("打电话给\(phoneNum)") }
③无参数有返回值
func readMessage() -> String { return "吃饭了吗?" }
④有参有返回值
func sum(num1 : Int, num2 : Int) -> Int { return num1 + num2 }
特例: 有多个返回值的函数(返回元组)
=> func getPerson() -> (name: String, age: Int) { return ("sz", 18) }
19.函数的使用注意:
1)外部参数和内部参数:
概念:在函数内部可以看到的参数,就是内部参数, 在函数外面可以看到的参数,就是外部参数
自swift3.0开始,如果你没有提供外部参数,则参数名称既是内部参数也是外部参数
(如果不想要外部参数,可以在参数名称前加_)
2)默认参数:
某些情况,如果没有传入具体的参数,可以使用默认参数 例如:test6(19) test6(b: 88)
注意: 如果使用默认值, 那么函数的接收参数可以是必传参数和可传参数的任意组合
3)可变参数
swift中函数的参数个数可以变化,它可以接受不确定数量的输入类型参数
我们可以通过在参数类型名后面加入(...)的方式来指示这是可变参数
注意:①它们必须具有相同的类型
② 可变参数, 就是外界直接传递过来多个参数
③ 函数内部, 是把这些参数当做一个数组来进行处理
4)经验总结:
①在函数内部, 默认接收到的参数被赋值给一个常量, 故不允许改变。
在swift3.0中, 如果想要改变,需要在函数内部, 使用var 间接改变 (不建议这样干)
② 默认情况下,函数的参数是值传递.如果想改变外面的变量,则需要传递变量的地址
=> Swift提供的inout关键字就可以实现地址传递
③ swift中函数可以嵌套使用 ( 即函数中包含函数,但是不推荐该写法)
20.函数的类型
1)函数类型的概念: 每个函数都有属于自己的类型,由函数的参数类型和返回类型组成
2)函数类型 引发的应用:
① 函数作为另外一个函数的参数
func exec(num1: Int, num2: Int, method: (Int, Int) -> Int) {
method(num1, num2) }
②函数作为另外一个函数的返回值
func getMethod(metName: String) -> (Int, Int) -> Int {}
21.枚举的使用
1)概念介绍
枚举定义了一个通用类型的一组相关的值,使你可以在你的代码中以一个安全的方式来使用这些值
在 C/OC 语言中枚举指定相关名称为一组整型值
Swift 中的枚举更加灵活,可以提供一个值是字符串,一个字符,或是一个整型值或浮点值
2)枚举类型的语法
使用enum关键词并且把它们的整个定义放在一对大括号内
enum SomeEnumeration {
// enumeration definition goes here
}
3)枚举类型的定义
enum CompassPoint {
case North
case South
case East
case West
}
注意:
①case关键词表明新的一行成员值将被定义
②不像 C 和 OC 一样,Swift 的枚举成员在被创建时不会被赋予一个默认的整数值
( 即 North,South,East和West不是隐式的等于0,1,2和3)
③定义枚举时也可以只用一个Case,成员以逗号分隔
enum Planet {
case Mercury, Venus, Earth, Mars, Jupiter, Saturn, Uranus, Neptune }
4)给枚举类型赋值
枚举类型赋值可以是字符串/字符/整型/浮点型
注意:如果有给枚举类型赋值,则必须在枚举类型后面明确说明具体的类型
enum CompassPoint : Int {
case North = 1
case South = 2
case East = 3
case West = 4
}
5)简单使用
根据枚举值获取"原始值”rawValue ,根据rawValue 获取 枚举值
注意: 枚举值仅仅是一个抽象符号, 并不代表任何类型; 如果想要绑定原始值, 必须指明枚举的类型
22.结构体
1.结构体的基本使用:
1)概念介绍
结构体(struct)是由一系列具有相同类型或不同类型的数据构成的数据集合
注意:结构体(struct)指的是一种数据结构,结构体是值类型,在方法中传递时是值传递
2)结构体的定义格式
struct 结构体名称 {
// 属性和方法
}
2.结构体扩充构造函数:
须知: 默认情况下, 结构体会自动创建一个"逐一构造器” 目的就是为了让所有"非可选"成员变量都能有值
但是为了让我们在使用结构体时更加的灵活,swift还可以对构造函数进行扩充 。
扩充的注意点 :
①创建构造函数, 不要func关键字
② 在扩充的构造函数中必须保证所有的非可选成员变量是有值的 (每一个扩充的构造函数都要这样做)
③ 扩充的构造函数会覆盖原有的构造函数(逐一构造器)
3.结构体扩充函数:
为了让结构体使用更加灵活,swift的结构体中可以扩充方法 :静态方法(类型方法)和实例方法
注意:如果自定义方法中, 想要修改成员属性的值,必须使用 mutating 关键字,因为, 结构体是"值”类型
补充: 扩充系统的结构体方法,Swift也能帮我们达成,这个机制,叫做extension
23.类的基本使用
1)类的介绍和定义
Swift也是一门面向对象开发的语言,面向对象的基础是类,类产生了对象
定义: class是Swift中的关键字,用于定义类
例如:
class 类名 : SuperClass { // 定义属性和方法 }
2)定义类时的注意事项:
原因:类是不会像结构体一样自动生成"逐一构造器"来保证"非可选"的成员属性必须有值
所以, 解决方案:
①将"非可选"的属性, 改为"可选”属性
② 给"非可选"属性设置默认值
③在构造函数中, 给"非可选"属性赋值
注意: 定义的类,可以没有父类.那么该类是rootClass
通常情况下,定义类时.继承自NSObject
3)类的构造函数
介绍: ①构造函数类似于OC中的初始化方法:init方法
②默认情况下创建一个类时,必然会调用一个构造函数
③ 即便是没有编写任何构造函数,编译器也会提供一个默认的构造函数。
④如果是继承自NSObject,可以对父类的构造函数进行重写
⑤注意:如果自定义了构造函数,会覆盖init()方法.即不在有默认的构造函数
基本使用:
① 类的"非可选"属性必须有值
② 如果不是在定义时初始化值,可以在构造函数中赋值
24.字典转模型(KVC)
1)字典转模型(初始化时传入字典)
去字典中取出的是,任意类型.可以通过as!转成需要的类型,再赋值(不可以直接赋值)
class Person: NSObject {
var name :String
var age :Int
// 自定义构造函数,会覆盖init()函数
init(dict : [String : Any]) {
name = dict["name"] as!String
age = dict["age"] as! Int
}
}
2)字典转模型(利用KVC转化)
注意: KVC并不能保证会给所有的属性赋值 => 属性需要有默认值 或为可选类型
(基本数据类型默认值设置为0,对象或者结构体类型定义为可选类型即可(可选类型没有赋值前为nil))
class Person: NSObject { // 结构体或者类的类型,必须是可选类型.因为不能保证一定会赋值
var name :String?
// 基本数据类型不能是可选类型,否则KVC无法转化
var age :
Int = 0
// 自定义构造函数,会覆盖init()函数
init(dict : [String : NSObject]) {
//必须先初始化对象(因为父类有帮你做初始化)
super.init()
// 调用对象的KVC方法字典转模型
setValuesForKeysWithDictionary(dict)
}
}
// 创建一个Person对象
let dict = ["name" : "sz", "age" : 18]
let p = Person(dict: dict)
25.类的析构函数
须知:
Swift会自动释放不再需要的实例以释放资源,具体是通过自动引用计数(ARC)处理实例的内存管理 当引用计数为0时,
系统会自动调用析构函数(不可以手动调用)
那我们学习析构函数的目的是:
通常在析构函数中释放一些资源(如移除通知等操作)
析构函数的写法:
deinit { // 执行析构过程 }
注意:析构函数必须写在类中。
class Person {
var name : String
var age : Int
init(name : String, age : Int) {
self.name = name
self.age = age
}
deinit{
print("Person-deinit")
}
}
var p : Person? = Person(name: "why", age: 18)
p = nil
26.类的属性和方法
1)存储属性:
存储属性是最简单的属性,它作为类实例的一部分,用于存储常量和变量,可以给存储属性提供一个默认值,也可以在初始化方法中对其进行初始化
2)计算属性:
计算属性并不存储实际的值,而是提供一个getter和一个可选的setter来间接获取和设置其它属性
计算属性`一般`只提供getter方法
如果只提供getter,而不提供setter,则该计算属性为只读属性,并且可以省略set{}
class Student : NSObject {
// 存储属性
var age : Int = 0
var name : String?
var chineseScore : Double = 0.0
var mathScore : Double = 0.0
// 计算属性
var averageScore : Double {
get {
return (chineseScore + mathScore) / 2
}
// 没有意义,因为之后获取值时依然是计算得到的
//newValue是系统分配的变量名,内部存储着新值
set {
self.averageScore = newValue
}
}
}
3)类属性
类属性是与类相关联的,而不是与类的实例相关联
(类属性的设置和修改,需要通过类来完成)
所有的类和实例都共有一份类属性.因此在某一处修改之后,该类属性就会被修改 类属性使用static来修饰
4)类的方法:
①对象方法
②类方法:
static 修饰 : 不能被子类重写
class 修饰 : 可以被子类重写
27.监听属性改变
须知:
在OC中我们可以重写set方法来监听属性的改变,
Swift中可以通过属性观察者来监听和响应属性值的变化。
通常是监听存储属性和类属性的改变.
(对于计算属性,我们不需要定义属性观察者,因为我们可以在计算属性的setter中直接观察并响应这种值的变化)
我们通过设置以下观察方法来定义观察者:
willSet:在属性值被存储之前设置。此时新属性值作为一个常量参数 被传入。该参数名默认为newValue
didSet:在新属性值被存储后立即调用。与willSet相同,此时传入的是属性的旧值,默认参数名为oldValue
注意:
① willSet与didSet只有在属性第一次被设置时才会调用,在初始化时,不会去调用这些监听方法
②可以给newValue / oldValue改名
class Person : NSObject {
var name : String? {
// 可以给newValue自定义名称
willSet(new){ // 属性即将改变,还未改变时会调用的方法
// 在该方法中有一个默认的系统属性newValue,用于存储新值
print(name)
print(new)
}
// 可以给oldValue自定义名称
didSet(old) { // 属性值已经改变了,会调用的方法
// 在该方法中有一个默认的系统属性oldValue,用于存储旧值
print(name)
print(old)
}
}
var age : Int = 0
var height : Double = 0.0
}
let p : Person = Person()
// 在赋值时,监听该属性的改变
// 在OC中是通过重写set方法
// 在swift中,可以给属性添加监听器
p.name = "wsz"
//p.name = "why"
28.swift中自动引用计数
Swift和OC一样,采用自动引用计数来管理内容
( 当有一个强引用指向某一个对象时,该对象的引用计数会自动+1,强引用消失则 -1 ,引用计数器为0则该对象自动销毁)
循环引用解决方案:
①weak : 和OC中的__weak一样是一个弱引用.当指向的对象销毁时,会自动将指针指向nil
②unowned : 和OC中的__unsafe_unretained.当对象销毁时依然指向原来的位置(容易引起野指针)
29.三大特性
封装
继承
多态 :父类指针, 指向子类, 来调用子类的方法
30.结构体和类的区别
①结构体有一个自动生成的初始化器。新实例中各个属性的初始值可以通过属性的名称传递到成员逐一初始化器之中。
②结构体是值类型, 类是引用类型(结构体的赋值意味着拷贝行为的发生)
③结构体不能继承
(意味着没有多态, 不具备运行时强制类型转换、使用析构器和使用引用计数器等能力)