目录
变量与数据类型
一、常量
//不要求编译时确定,如果声明时没有值需要指定类型
let a: Int = 10
//可以省略类型说明和结尾的分号
let a = 10
二、变量
var isClick:Bool = false //可以省略类型说明和结尾的分号
var a:Int = 0;
var a:Float = 10.0;
var a:Double = 10.0;
var cha:Character = "a" // 字符类型必须指定类型
var str:String = "hello world"
var array:Array = [1, 2, 3, 4]
var dic : Dictionary = ["key1":"value1", 1:"value2"]
var set:Set = ["age","weight"]
// 元组
let error = (404, "Not Fount")// error.0
let error = (code:404, msg:"Not Fount")//error.code
//对象类型
let p:Person = Person(name:"xiaoming",age:18)
//匿名函数:闭包表达式,可以作为参数,尾随闭包的调用
var blk:(Int,Int)->Int = {(a, b)->Int in return a + b} // var result = blk(10,20)
//函数
func sum(a:Int, b:Int)->Int {return 10}
func goToWork(at time:String){} // goToWork(at:"10:00")
三、数据类型
1、String
swift中的字符串类型是结构体,是值类型
值类型的特点是:将a赋值给b之后,对b进行修改不会影响a
//1、长度小于等于15的字符串存储于str的内存中
var str:String = "0123456789"
//2、append后长度仍小于等于15的字符串 仍位于变量的内存
str.append("A")
//3、长度大于15的字符串位于常量区(常量区的内存在编译时分配,程序运行时无法更改)
var str = "0123456789ABCDEF"
//4、不管之前长度是否大于15,append后长度大于15的字符串 会存储于堆空间
// 位于堆空间后,比如a = 堆空间地址 b = 堆空间地址,当 b 修改字符串时,会产生实际的复制。也不会影响a
// 字符串长度过长时,使用上是值类型,实际是引用类型
str.append("ABCDEF")
常用方法:
// 字符串的拼接
let str1 = "hello";
let str2 = "world"
let newStr = str1 + " " + str2;
// 字符串子串
let startIdx = newStr.index(newStr.startIndex, offsetBy: 6)
let endIdx = newStr.index(newStr.endIndex, offsetBy: -1)
let subStr = newStr[startIdx...endIdx];
2、Array
Swift中的数组也是结构体,是值类型
var arr:Array = [1,2,3,4]
var arr1 = arr
arr1.append(6)
//arr1的改变并不会影响arr
//arr1 = [1, 2, 3, 4, 6], arr = [1, 2, 3, 4]
print("arr1 = \(arr1), arr = \(arr)")
PS:Array在底层其实是引用类型 但实际使用时是结构体
行为上是值类型 本质是引用类型只是苹果隐藏了这一层
常用方法:
var array = [0, 1, 2, 3, 4, 5, 6]
// 常用属性
array.count;
array.first;
array.last;
array[0];
//尾部操作
array.append(7);
array.popLast();
//通用操作
array.insert(7, at: array.count);
array.remove(at: array.count-1)
//子数组
let subArray = array[2...array.count-1];
print(subArray);// [2, 3, 4, 5, 6]
let startIdx = array.index(array.startIndex, offsetBy: 2)
let endIdx = array.index(array.endIndex, offsetBy: -1)
let subArray2 = array[startIdx...endIdx]
print(subArray2)// [2, 3, 4, 5, 6]
let array = [0, 1, 2, 3, 4, 5, 6]
// 映射
let newArray = array.map({(item) in
return item * 2
})
// [0, 2, 4, 6, 8, 10, 12]
print(newArray)
// 过滤
let filterArray = array.filter({(item) in
return item % 2 == 0
})
// [0, 2, 4, 6]
print(filterArray)
// 求和
let reduce = array.reduce(0, {(result, element) in
return result + element;
})
// 21
print(reduce)
3、Dictionary 和 Set 也是结构体类型,也是值类型
常用方法:
// 初始化一个字典
var emptyDict = [String: Int]()
// 初始化一个字典
var map:[String : Any] = [
"name" : "xiaoming",
"age" : 18
]
// ["age": 18, "name": "xiaoming"]
print(map)
// 1-1、获取,如果不存在就返回缺省值
var name = map["name",default: "Tom"]
//xiaoming
print(name)
// 1-2、获取,因为可能是空所以返回可选项
name = map["name"]
//Optional("xiaoming")
print(name)
// 2、修改
map["name"] = "Alice"
// ["age": 18, "name": "Alice"]
print(map)
// 3、删除一个key
map.removeValue(forKey: "age")
// ["name": "Alice"]
print(map)
// 4、增加一个key
map["age"] = 19
// ["age": 19, "name": "Alice"]
print(map)
var dic:[String : Any] = [
"name" : "xiaoming",
"age" : 18
]
// 映射
let array = dic.map({(item) in
// return (item.key, item.value)
return item
})
// [(key: "name", value: "xiaoming"), (key: "age", value: 18)]
print(array)
// 过滤
let newDic = dic.filter({element in
if element.key == "name" {
return true
} else {
return false
}
})
// ["name": "xiaoming"]
print(newDic)
// 求和
let result = dic.reduce(0, {(result, element) in
if element.value is Int {
return result + (element.value as! Int)
} else {
return result + 0
}
})
// 18
print(result)
4、元组是一个复合类型,也是值类型
四、Any AnyObject AnyClass
Any 任意类型:对象类型,int类型等
AnyObject 任意对象类型:实例对象
AnyClass 任意类对象类型
AnyObject.type 相当于 AnyClass 任意类对象类型
Person 比如 let p: Person = Person(); 用于指定实例对象p的类型
Person.type 比如 let pClass : Person.type = Person.self; 用于指定类对象pClass的类型
注意:
Person.self 是类对象,
Person.type 是指定类型的(类对象的类型),
Person 是指定类型的(实例对象的类型)
五、self 与 Self
小写的self
在实例方法中指代实例对象
在静态方法中指代类对象
大写的Self
1、作为返回值,用于指代当前类型
2、在静态方法中指代类对象, Self.age静态属性 self.age也是静态属性
闭包
一、什么是闭包?
外层函数 + 内层函数 + 内层函数访问外层函数的变量
二、闭包的作用?
1、捕获变量,在函数调用结束后仍能访问函数内部的变量:比如计数器
2、代码模块化,比如返回一个包含了多个方法的元组或对象:比如计算器
3、异步任务,函数调用结束后还能执行捕获的闭包表达式:网络请求
三、循环引用
跟OC一样,Swift也是采取基于引用计数的ARC内存管理方案(针对堆空间)
强引用(strong reference):默认情况下,引用都是强引用
弱引用(weak reference):通过weak定义弱引用
无主引用(unowned reference):通过unowned定义无主引用
class Person {
var blk:Blk?
var name:String
init(blk: Blk? = nil, name: String) {
self.blk = blk
self.name = name
}
deinit {
print("deinit")
}
}
let p = Person(name: "xiaoming")
//不会调用deinit方法,p对象无法释放
p.blk = {(num) in
let pname = p?.name ?? ""
print("p 的 name是 \(pname)")
}
p.blk?(9)
//会调用deinit方法
p.blk = { [weak p] (num) in
let pname = p?.name ?? ""
print("p 的 name是 \(pname)")
}
p.blk?(9)
四、逃逸闭包
非逃逸闭包、逃逸闭包,一般都是当做参数传递给函数
非逃逸闭包:闭包调用发生在函数结束前,闭包调用在函数作用域内
逃逸闭包:闭包有可能在函数结束后调用,闭包调用逃离了函数的作用域,需要通过@escaping声明
typealias Blk = ((Int)->Void)
func handleEvent(blk: @escaping Blk) {
DispatchQueue.global().async {
blk(9)
}
}
handleEvent { num in
print("\(num)")
}
可选项
一、空判断和强制解包
let a:Int? = 10
if a! = nil {
let num = a!
}
二、自动解包
1、可选项绑定自动解包
// num的作用域是块作用域
if let num=a, num>0 {
}
while let num=a, num>0 {
}
// userName的作用域是函数作用域
// 字典返回的是可选项
guard let userName = info["userName"] {
return
}
print(userName)
2、! 自动解包
let a:Int? = 1
let b:Int! = 2
// b可以赋值给c,a不行
let c:Int = b
三、空合并运算符
let a:Int? = 1
let b:Int? = 2
// a不为nil返回a,a为nil返回b.
// 返回的类型与b相同
let c = a ?? b
四、多重可选项
看下图分析:
num1 == num3; // true 非空的可选项比较会递归解包
num2 == num4; // false 空也是有类型的,它俩类型不同所以不相等。
这里重点介绍下num5,num5是非空的,所以比较时会递归解包
num2 == num5; // ture
五、可选链
let person:Person? = Person()
//如果不是可选类型会包装成可选类型
let age = person?.age // optional
let age = person!.age // int
// 如果是可选类型不会重复包装
// car 是可选类型
let car = person?.car //optional
let car = person!.car //optional
// 如果person是nil就会终止后续操作
person?.run()
let num:Int? = 5
let num:Int? = nil
//num是nil会终止后续操作,不会赋值10
num? = 10
六、可选项的本质是枚举
1、举例
//可选项
var num:Int? = nil
print(num) // nil
num = 10
print(num) // Optional(10)
// 枚举
var num1:Optional<Int> = nil
print(num1) // nil
num1 = 10
print(num1) // Optional(10)
// .none 就是nil
var num2:Optional<Int> = .none
print(num2) // nil
num2 = .some(10)
print(num2) // Optional(10)
2、自己实现
enum MYOptional<T> : ExpressibleByNilLiteral & ExpressibleByIntegerLiteral & CustomStringConvertible{
case none
case some(T)
// 字面量协议: ExpressibleByNilLiteral
init(nilLiteral: ()) {
self = .none
}
// 字面量协议:ExpressibleByIntegerLiteral
init(integerLiteral : Int) {
self = .some(integerLiteral as! T)
}
// CustomStringConvertible 协议
var description: String {
switch self {
case .none:
return "nil"
case let .some(val):
return "Optional(\(val))"
}
}
}
var num3:MYOptional<Int> = nil
print(num3) // nil
num3 = 10
print(num3) // Optional(10)
var num4:MYOptional<Int> = .none
print(num4) // nil
num4 = .some(10)
print(num4) // Optional(10)
枚举/结构体/类
枚举
一、原始值
内存占用1字节
enum Season:Int{
case spring = 1,summer,autumn,winter
//enum 的 rawValue的本质是计算属性
var rawValue: Int{
get{
switch self {
case .spring:
return 10
case .summer:
return 20
case .autumn:
return 30
case .winter:
return 40
}
}
}
}
let value = Season.spring.rawValue
print(value)// 10
二、枚举关联值
内存占用 1字节 + n字节
enum Score{
case points(Int)
case grade(Character)
}
var sc = Score.points(96)
sc = .grade("A")
// 关联值绑定
switch sc {
case let .points(i):
print(i,"points")
case let .grade(i):
print(i,"grade")//A grade
}
三、枚举内也可以定义方法
enum Season:String {
case Spring, Summer, Autum, Winter
func show(){
print("rawValue is \(rawValue)")
}
}
结构体
结构体是值类型 内存分布:栈空间
let 对结构体变量和类变量的不同,理解let的本质
结构体没有便捷初始化器
具体的样式,参考下面的类对象
类
一、基本结构
类是引用类型
内存分布:堆空间(16的倍数) 前8字节isa 再8字节引用计数。let p = Person() p占用8个字节
嵌套的类型不占用外部类型的空间
Person对象
class Animal {
var age:Int = 0
init(age: Int) {
self.age = age
}
}
class Person : Animal {
//存储实例属性
var name:String = ""{
willSet{
print(newValue)
}
didSet{
print(oldValue)
}
}
//计算实例属性 必须是var,本质是方法
//计算属性必须执行类型
var nameAndAge:String {
set{
print(newValue)
}
get{
return "\(self.name) : \(self.age)"
}
}
//存储类型属性
static var armNum:Int = 2 {
willSet {
}
didSet {
}
}
static var legNum:Int{
set {
}
get {
return 2
}
}
//init方法(重要):指定初始化器 + 便捷初始化器,以及父子类中有哪些规则
//指定初始化器不能互相调用,一个指定初始化器就是一个出口
// 指定初始化器
init(age: Int, name: String) {
//初始化的第一阶段
self.name = name
super.init(age: 0)
//初始化的第二阶段
self.age = age
}
//便捷初始化器
convenience init(name:String) {
self.init(age: 0, name: "xiaoming")
}
// 实例方法
func tellMeYourName(use language:String) -> String {
if language == "En" {
return "my name is \(self.name)"
} else {
return "我的名字叫 \(self.name)"
}
}
// 静态方法
static func runDes (){
print("run is a ability of human")
}
// deinit 方法不适用于结构体和枚举,因为它们是值类型,而不是引用类型
// deinit方法:先子类后父类
deinit{
}
}
let p = Person(age: 18, name: "Jim")
p.tellMeYourName(use: "En")
Person.runDes()
Person.legNum
二、实例对象与类对象
class Person {}
let p :Person = Person()
let cls :Person.type = Person.self
// p 对应OC中的实例对象
// Person.self 对应OC中的类对象
// Person.type 对应OC中的Class
// Person 与 Person.self 都能调用类方法
// let pcls : Person.type = Person.self; 但是不能 let pcls:Person.type = Person
共同
一、扩展
扩展可以给类添加方法:方法、计算属性、下标
扩展不能重写类的方法:如果能重写是不是就覆盖掉了系统类的实现,那些使用系统类方法的地方就会有问题
扩展不能添加指定初始化器:Swift中的初始化是一个严格的过程,不允许在扩展中添加,保证初始化的一致性
class Stack<E> {
var elements = [E]()
func push(_ element: E) {
elements.append(element)
}
func pop() -> E {
elements.removeLast()
}
func size() -> Int {
elements.count
}
}
// 给stack类扩展遵守Equatable协议
// E 类中的泛型在扩展中仍然可以使用
// 满足某些条件才会有扩展
extension Stack : Equatable where E : Equatable {
static func == (left: Stack, right: Stack) -> Bool {
left.elements == right.elements
}
}
// 扩展系统类
extension Array {
subscript(nullable idx: Int) -> Element? {
if (startIndex..<endIndex).contains(idx) {
return self[idx]
}
return nil
}
}
二、访问控制
模块:指的是独立的代码单元,框架或应用程序会作为一个独立的模块来构建和发布。在 Swift 中,一个模块可以使用 import 关键字导入另外一个模块
在访问权限控制这块,Swift提供了5个不同的访问级别(以下是从高到低排列, 实体指被访问级别修饰的内容)
open:允许其他模块访问、继承、重写(open只能用在类、类成员上)
public:其他模块中访问,不允许其他模块进行继承、重写
internal:只允许在当前模块访问、继承、重写,不允许其他模块访问
fileprivate:只允许在定义实体的源文件中访问
private:只允许在定义实体的封闭声明中访问,都在文件下的话private 和 fileprivate 作用相同
绝大部分实体默认都是internal 级别
协议
一、一个完整的协议格式
protocol Life {
//Property in protocol must have explicit { get } or { get set } specifier
var age:Int{get}
var name:String{get set}
init(age:Int, name:String)//也可以int?(xxx) int!(xxx)
func run()
mutating func grow()
static func walk()
}
//协议的继承
protocol Dog : Life {
func fake()
}
protocol Person : Life {
var car:String{get}
}
protocol Teach {
var studens:Array<Any>{get}
}
//协议的使用
class Teacher : Person & Teach {
xxx
}
二、带关联类型的协议
protocol Runable {
associatedtype Speed // 关联类型
func run(s:Speed)
}
//带关联类型和Self的协议只能作为泛型的约束,不能作为参数类型和返回值类型
//xy:参数类型和返回值类型需要确定的类型,不能包含不透明类型(协议内的关联类型为不透明类型或者叫不确定类型)
//要想带关联类型和Self的协议作为参数类型和返回值类型可以使用some关键字
func setObj(obj:some Runable) {
}
三、给协议添加扩展
//提供默认实现
extension BinaryInteger {
func isOdd() -> Bool { self % 2 != 0 }
}
泛型
一、泛型就是将类型作为参数,或者说是类型的占位
// 方法与泛型
func test<T>(num:T) -> T {
//返回值的类型也应该是T
// 不能返回 num + 10,因为不知道num是否支持+号运算,也不知道(num+10)是否是T类型
// 泛型在使用时要将其作为一个特定类型使用它就是T类型而不是其它任何类型。
return num
}
test(num:10)
//枚举与泛型
enum Score<T>{
case point(T)
case grade(String)
}
let Score<Int>.point(10)
//类与泛型
class Stack<T> {
var elements = [T]()
func push(element:T) {
elements.append(element)
}
}
let st = Stack<Int>()
st.push(element:10)
//类与泛型与协议
protocol Stackable {
associatedtype E // 关联类型
mutating func push(element: E)
}
class Stack<T> : Stackable {
//指定协议内的关联类型为具体类型
//typealias E = Int
//func push(element:E) {}
//指定关联类型为泛型
//typealias E = T
//func push(element:E) {}
//自定指定关联类型为Int
//func push(element: Int) {}
// 自定指定关联类型为T
func push(element: T) {}
// typealias 就是一个别名
}
二、可以给泛型指定约束
// 用协议约束泛型
func compaire<T : Equatable>(num1:T, num2:T) -> Bool {
return num1 == num2
}
// 用带有关联类型的协议约束泛型
protocol Runable {
associatedtype Speed // 关联类型
func run(s:Speed)
}
func setObj<T:Runable>(obj:T) {
}
class Animal : Runable {
//指定关联类型为Int
func run(s: Int) {}
}
let ani = Animal()
setObj(obj:ani)
三、some关键字
带关联类型和Self的协议只能作为泛型的约束,不能作为参数类型和返回值类型
xy:参数类型和返回值类型需要确定的类型,不能包含不透明类型(协议内的关联类型为不透明类型或者叫不确定类型)
// 下面两个方法会报错
//Protocol 'Runnable4' can only be used as a generic constraint because it has Self or associated type requirements
func get() -> Runable{
return Animal() as! Runable
}
//Protocol 'Runnable4' can only be used as a generic constraint because it has Self or associated type requirements
func setobj(obj:Runable) {
}
要想带关联类型和Self的协议作为参数类型和返回值类型可以使用some关键字
func get() -> some Runable{
return Animal() as! Runable
}
func setobj(obj:some Runable) {
}
还可以使用泛型解决
func get<T:Runable> () -> T {
}
func set<T:Runable>(obj:T) {
}
模式匹配
一、Switch 语句
let day = "Monday"
switch day {
case "Monday":
print("It's the start of the week")
case "Friday":
print("It's almost the weekend")
default:
print("It's another day")
}
二、元组解构
let person = ("Alice", 30)
let (name, age) = person
print("Name: \(name), Age: \(age)")
三、Optional绑定
let optionalValue: Int? = 42
if let value = optionalValue {
print("The value is \(value)")
} else {
print("No value")
}
四、联值绑定
1、关联值绑定
var sc = Score.points(96)
// 关联值绑定
switch sc {
case let .points(i):
print(i,"points")
case let .grade(i):
print(i,"grade")
}
2、值绑定
let values: [Int?] = [2, 3, nil, 5]
for case let value? in values {
print("Found non-nil value: \(value)")
}
五、类型模式匹配
let someValue: Any = "Hello, Swift"
if someValue is String {
print("It's a string")
} else if someValue is Int {
print("It's an integer")
}
六、where 子句
let temperature = 25
switch temperature {
case let temp where temp < 0:
print("It's freezing!")
case let temp where temp >= 0 && temp < 20:
print("It's cold")
default:
print("It's warm")
}
其它
mutating的使用
subscript的使用
final的使用:禁止被子类重写和继承
inout的本质:地址传递
// 计算属性 和 设置了属性观察器的属性
copy in copy out(会产生一个副本)
单利对象
public static let share = FileManager()
private func init(){}
进度:15-02
行者常至,为者常成!