函数、枚举、可选项、结构体、闭包、属性、chushih

文章目录
  1. 1. 函数
  2. 2. 枚举
  3. 3. 可选类型
  4. 4. 结构体
  5. 5. 闭包
  6. 6. 属性
    1. 6.1. inout的本质总结
  7. 7. 继承
  8. 8. 初始化器
    1. 8.1. 初始化器相互调用规则:
    2. 8.2. 两段式初始化:
    3. 8.3. 重写:
    4. 8.4. 自动继承:
    5. 8.5. required
    6. 8.6. 属性观测器
    7. 8.7. 可失败初始化器
    8. 8.8. 反初始化器
  9. 9. 协议
    1. 9.1. 协议中的属性
    2. 9.2. Any 、AnyObject
  10. 10. 错误处理
  11. 11. 泛型
  12. 12. String、Array
  13. 13. 扩展
  14. 14. 访问控制
  15. 15. 指针

函数 、枚举、可选项等学习记录

函数

  1. 形参默认是let
  2. 函数体是单一表达式,隐式返回
  3. 返回元组,多参数返回
  4. 函数的注释:

    1
    2
    3
    4
    5
    - Parameter v1:
    - Parameter v2:

    - Returns:
    - Note:
  5. 参数标签:下划线可以省略参数标签

  6. 参数默认值:由于swift拥有参数标签,对默认参数没有从右向左的顺序的限制
  7. 可变参数: 类型后面三个点,一个函数只能有一个可变参数,紧跟在可变参数后面参数不能省略参数标签
  8. 输入输出参数: 类型签名添加inout, inout参数不能有默认值,本质是引用
  9. 函数重载:参数个数|参数类型|参数标签不同都构成函数重载,返回值不构成函数重载
  10. 内联函数:不会内联的函数包括:函数体比较长,包含递归、动态派发@inline(never) @inline(__always)
  11. 函数类型,条用的时候不需要参数标签
  12. typealias 用来给类型其别名 typealias Byte = Int8

枚举

  1. 枚举的用法

    1
    2
    3
    4
    5
    6
    enum Direction{
    case north
    case south
    case east
    case west
    }
  2. 关联值:枚举的成员值和其他类型的值关联存储在一起

  3. 原始值:枚举成员可以使用相同类型的默认值预先对应,这个默认值叫做原始值,定义方法:在枚举类型后面指定类型
  4. 所以:枚举相关的名称有:成员值、原始值(不占用枚举变量的内存)、关联值
  5. 隐式原始值: 如果枚举的原始值类型是Int、Stirng,swift会自动分配原始值
  6. 递归枚举: indirect关键字指定
  7. MemoryLayout : stride分配内存, size 实际用到的大小、 alignment对齐参数

可选类型

  1. 在类型后面添加问号来定义一个可选类型
  2. 如果为nil:那么它是一个空盒子
  3. 如果不为nil,那么盒子里面装的是被包装类型的数据
  4. 使用感叹号取出盒子中包装的值取出来—强制解包
  5. 如果对nil的可选项进行强制解包会产生运行错误。
  6. 判断可选项是否包含值 if num != nil
  7. 可选绑定:如果包含值,自动解包,赋值给临时变量,返回true,否则返回false
  8. 空合并运算符 a ?? b。a是可选项、b可以是可选项,也可以不是、ab类型相同、如果b不是可选项,返回a会自动解包。
  9. guard 如果条件为false时就执行大括号中的代码;如果条件为true,跳过guard语句;guard绑定的常量变量外层作用域中也能使用。
  10. 可选的本质是枚举

结构体

  1. 一旦结构体定义时定义了初始化器, 编译器就不会再帮自动生成初始化器
  2. 如果类的所有成员都在定义的时候指定了初始值,编译器会为类生成无参的初始化器
  3. 结构体是值类型,类是引用类型
  4. 值类型赋值,是直接将所有的内容拷贝一份,是深拷贝
  5. 引用类型赋值: 是将内存地址拷贝一份
  6. 对象的堆空间的申请过程

    1
    2
    3
    4
    5
    Class.__allocating_init()
    libswiftCore.dylib: _swift_allocObject_
    libswiftCore.dylib: _swift_slowAlloc_
    libsystem_malloc.dylib: malloc
    ``
  7. malloc函数分配的内存大小是16的倍数

  8. 通过class_getInstancSize可以得知:类的对象至少需要占用多少内存
  9. 方法占用内存吗?不占用

闭包

闭包的结构如下:

1
2
3
4
{
(参数列表)-> 返回值 in
函数体代码
}
  1. 尾随闭包简化函数调用,增强可读性。闭包作为函数的最后一个参数使用时
  2. 闭包: 一个函数和它所捕获的变量、常量环境组合起来,称为闭包
  3. 自动闭包
    1
    2
    3
    4
    5
    1. @autoclosure 会将20封装成闭包 {20}
    2. 只支持()->T格式的函数
    3. 空合并运算符只用了autoclosure技术
    4. 有无autoclosure构成了函数重载
    5. 使用autoclosure的地方最好注明,这个值会被延迟执行

属性

1
2
3
4
5
6
7
8
9
10
11
struct Circle {
var radius: Double
var diameter: Double {
set(newDiameter) {
radius = newDiameter/2
}
get {
radius*2
}
}
}
  1. 定义计算属性只能用var,不能用let
  2. 延迟存储属性: 第一次用到的时候才进行初始化,lazy属性必须是var,let必须在实例初始化完成之前就拥有值,所以lazy和let是互斥的。多线程不安全
  3. 当结构体包含一个延迟存储属性时,只有var才能访问延迟存储属性,因为延迟粗才能属性会改变结构体的内存
  4. 可以为非lay的var存储属性设置属性观察器

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    struct Circle {
    var radius: Double {
    willSet {
    print("willset")
    }
    disSet {

    }
    }
    }
  5. willSet 会传递新值,didSet会传递旧值

  6. 在初始化器中设置属性值不会触发willSet和didSet
  7. 在属性定义时设置初始值也不会触发willSet和didSet
  8. 属性观测器、计算属性的功能同样可以应用在全局变量、局部变量上

inout的本质总结

如果实参有物理内存地址,且没有设置属性观测器: 直接将实参的内存地址传入函数(实参进行引用传递)

如果实参是计算属性或者设置了属性观测器: 采用copy in copy out的做法

  1. 调用该函数时,先复制实参的值,参数副本[get]
  2. 将副本的内存地址传入函数(副本进行引用传递), 在函数内部可以修改副本的值
  3. 函数返回后,再将副本的值覆盖实参的值[set]

所以: inout的本质就是引用传递(地址传递)

可以通过static 定义类型属性,在类中,也可以通过class定义类型属性

类型属性: 类型存储属性默认是lazy的,多线程安全,不需要设定初始值,应为类对象没有向实例一样的初始化器。

继承

重写属性:

  1. 子类可以将父类的属性(存储、计算)重写为计算属性
  2. 子类不可以将父类属性重写为存储属性
  3. 只能重写var属性,不能重写let属性
  4. 子类重写后的属性权限不能小于父类属性的权限
    1. 如果父类属性是只读的,那么子类重写后的属性可以是只读的,也可以是可读写的
    2. 如果父类属性是可读写的,那么子类重写后的属性必须是可读写的
  5. 可以在子类中为父类属性(除只读计算属性、let属性)增加属性观测器
  6. 被final修饰的方法、小标、属性禁止被重写,被final修饰的类禁止被继承

初始化器

  1. 类、结构体、枚举都可以定义初始化器
  2. 类有两种初始化器: 指定初始化器、便捷初始化器

初始化器相互调用规则:

  1. 指定初始化器必须从他的直系父类条用指定初始化器
  2. 便捷初始化器必须从相同的类中调用另一个初始化器
  3. 便捷初始化器最终必须调用一个指定初始化器

两段式初始化:

  1. 外层调用指定、便捷初始化器
  2. 分配内存给实例,但并未初始化
  3. 指定初始化器确保当前类定义的存储属性都初始化
  4. 指定初始化器调用父类的指定初始化器,不断向上调用,形成初始化器链

第二阶段:设置新的存储属性

  1. 从顶部初始化器往下,链中每一个指定初始化器有机会进一步定制实例
  2. 初始化器能够使用self
  3. 最终,链中任何便捷初始化器都有机会定制实例及使用self

重写:

如果子类写一个匹配父类便捷初始化器的初始化器,不用加水override,因为父类的便捷初始化器永远不会通过子类直接调用,因为,严格来说,子类无法重写父类的便捷初始化器。

自动继承:

  1. 如果子类没有自定义任何指定初始化器,会自动继承父类所有指定初始化器
  2. 如果子类提供父类所有指定初始化器的实现(用么通过方式1继承,要么重写),子类自动继承所有父类便捷初始化器
  3. 就算子类添加了更多的便捷初始化器,这些规则仍然适用
  4. 子类以便捷初始化器的形式重写父类的指定初始化器,也可以作为满足规则2的一部分。

required

  1. 用required修饰指定初始化器,表明所有子类都必须实现该初始化器(通过继承或者重写实现)
  2. 如果子类重写了required初始化器,也不需加上required ,不用加override

属性观测器

父类的属性在它自己的初始化器中赋值不会触发属性观测器,单在子类的初始化器中赋值会触发

可失败初始化器

  1. 不允许同时定义参数标签、参数个数、参数类型相同的可失败初始化器和非可失败初始化器
  2. 可以使用init!定义隐式解包的可失败初始化器
  3. 可失败初始化器可以调用非可失败初始化器、非可失败初始化器调用可失败初始化器需要进行解包
  4. 如果初始化器调用一个可失败初始化器导致失败,那么整个初始化过程都失败,并且之后的代码都停止执行
  5. 可以用一个非可失败初始化器重写一个可失败初始化器,反过来不行

反初始化器

  1. deinit不接受任何参数,不能写小括号,不能自行调用
  2. 父类的deinit能被子类继承
  3. 子类的deinit实现执行完毕后,会调用父类的deinit

协议

协议中的属性

  1. 必须用var关键字,为啥?
  2. 实现协议时的属性权限不小于协议中定义的属性权限
  3. get、set 用var存储属性或get、set计算属性实现
  4. 协议定义get 用任何属性都可以实现

协议中使用static定义类型方法、类型属性、类型下标

  1. 协议中可以定义初始化器init
  2. 非final类实现时必须加上required
  3. 如果从协议实现的初始化器,刚好是重写了父类的指定初始化器,那么这个初始化必须同时加上requied 、 override

Any 、AnyObject

  1. Any 任意类型
  2. AnyObject 任意类类型,协议后天写上AnyObject代表只有类能准守这个协议
  3. 在协议后面写上class也代表只有类能准守这个协议
  4. is用来判断是否某种类型, as 用作强制类型转换
  5. X.self、 X.Type、 AnyClass ,X.self 是一个原类型的指针,metadata存放这类型相关的信息
  6. X.self 属于X.type类型
  7. Self 一般用作返回值类型,限定返回值跟方法调用者必须是同一类型

错误处理

  1. throw抛出自定义Error
  2. 可能抛出Erro的函数必须加上throws声明
  3. 需要使用try调用可能会抛出Error的函数
  4. 使用do catch 捕获Error
  5. rethrow 函数本身不会抛出异常,但调用闭包参数抛出异常
  6. defer 用来定义任何方式(抛出错误、return)离开代码块前必须执行的代码
  7. defer的顺序于定义的顺序相反
  8. 不得不实现,但不希望别人调用,使用fatalError
  9. 局部作用域使用do 关键字

泛型

将类型参数化,提高代码的复用率,减少代码量

  1. 泛型函数赋值给变量,必要有意思 var fn:(Int,Double)->() = test
  2. 关联类型(给协议中用到的类型定义一个占位名称)
  3. 类型约束
  4. 如果协议中有关联类型,作为返回值,报错? 因为不知道具体返回的类型中的关联类型是什么,所以就有了不透明类型
  5. 使用some 声明一个不透明类型
  6. some限制只能返回一种类型
  7. 可选项的本质是枚举 。none 0、 some 1

String、Array

  1. 代码区、 常量区、全局区、 堆空间、栈空间、 动态库
  2. 符号的延迟绑定通过dyld_stub_binder完成

扩展

如果希望自定义初始化器的同时,编译器也能够生成默认初始化器

  1. 可以在扩展中编写自定义初始化器
  2. required 初始化器不不能卸载扩展中
  3. 扩展可以给协议提供默认实现, 也间距实现可选协议的效果
  4. 扩展可以给协议扩充《协议中从未声明过的方法》
  5. 复合条件才能扩展

访问控制

  1. 五个访问级别 open、 public、 internal、 fileprivate、private
  2. 访问级别使用的规则: 一个实体不可以被更低访问级别的实体定义
  3. 元组类型的访问级别是所有成员类型最低的那个
  4. 泛型类型的访问级别是类型的访问级别以及所有泛型类型参数的访问级别中最低的那个
  5. 子类重写成员的访问级别>=子类的访问级别或者父类被重写成员的访问级别
  6. 可以给setter单独设置一个比getter更低的访问级别,泳衣限制写的权限
  7. 不能给枚举的每个case单独设置访问级别,每个case自动接收enum的访问级别
  8. 协议中定义的要求自动接收协议的访问级别,不能单独设置访问级别,public协议定义的要求也是public的
  9. 如果没有显示设置扩展的访问级别,扩展添加的成员自动接收扩展的访问级别,可以单独给扩展添加的成员设置访问级别
  10. 不能给用于准守协议的扩展显示设置扩展的访问级别
  11. 在同一文件中的扩展,可以写成类似多个部分的类型声明,在原本的声明中声明一个私有成员,可以在统一文件的扩展中访问它
  12. 将方法赋值给let或者var ,这个也比较有意思
    1
    let fn3: (Persion) -> ((Int) -> ()) = Persion.run ///将实例方法赋值给变量

指针

  1. UnsafePointer<Pointee> 类似于 const Pointee *
  2. UnsafeMutablePointer<Pointee> 类似于 Pointee *
  3. UnsafeRawPointer 类似于 const void *
  4. UnsafeMutableRawPointer 类似于 void *
  1. 获取变量的指针 withUnsafeMutablePointer(to: &age){return $0}
  2. 获取执行堆空间实例的指针

    1
    2
    var ptr = withUnsafePointer(to: &person){UnsafeRawPointer($0)}
    var heapPtr = UnsafeRawPointer(bitPattern: ptr.load(as:Uint.self))
  3. 创建指针 var ptr = malloc(16)'var ptr = UnsafeMutableRawPointer.allocate(byteCount:16,alignment:1)`

  4. 指针之间的切换ptr.assumingMemoryBound(to:Int.self).pontee = 11
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
/// 测试泛型指针
func testPointer() -> () {
var age = 10
func test1(_ ptr: UnsafeMutablePointer<Int>) {
ptr.pointee += 10
}
func test2(_ ptr: UnsafePointer<Int>) {
print(ptr.pointee)
}

test1(&age)
test2(&age)
print(age)
}
/// 测试raw 指针
func testRawPointer() -> () {
var age = 10
func test3(_ ptr: UnsafeMutableRawPointer) {
ptr.storeBytes(of: 20, as: Int.self)
}
func test4(_ ptr: UnsafeRawPointer) {
print(ptr.load(as: Int.self))
}

test3(&age)
test4(&age)
print(age)
}

///获取指向自动变量的指针
func getAutoVarPointer() -> () {
var age = 11
var ptr1 = withUnsafeMutablePointer(to: &age) { $0 }
var ptr2 = withUnsafePointer(to: &age) {$0}
ptr1.pointee = 22
print(ptr2.pointee)
print(age)


var ptr3 = withUnsafeMutablePointer(to: &age) { UnsafeMutableRawPointer($0)}
var ptr4 = withUnsafePointer(to: &age) { UnsafeRawPointer($0) }
ptr3.storeBytes(of: 33, as: Int.self)
print(ptr4.load(as: Int.self))
print(age)
}

///获取指向堆空间实例的指针
func getHeapVarPointer() {
class Persion { }
var persion = Persion()
var ptr = withUnsafePointer(to: &persion) { UnsafeRawPointer($0) }
var heapPtr = UnsafeRawPointer(bitPattern: ptr.load(as: UInt.self))
print(heapPtr!)
print(persion)
}

///使用地址创建指针
func createAddressPointer() -> () {
var ptr = UnsafeRawPointer(bitPattern: 0x1122)

var ptr1 = malloc(16)
//存
ptr1?.storeBytes(of: 11, as: Int.self)
ptr1?.storeBytes(of: 22, toByteOffset: 8, as: Int.self)

//取
print(ptr1?.load(as: Int.self))
print(ptr1?.load(fromByteOffset: 8, as: Int.self))

//销毁
free(ptr1)
}

/// 使用allocate 创建raw指针
func createRawPointerByAllocate() {
var ptr2 = UnsafeMutableRawPointer.allocate(byteCount: 16, alignment: 1)

//存
ptr2.storeBytes(of: 11, as: Int.self)
ptr2.advanced(by: 8).storeBytes(of: 22, as: Int.self)

// 读
print(ptr2.load(as: Int.self))
print(ptr2.advanced(by: 8).load(as: Int.self))
ptr2.deallocate()
}

///使用allocate 创建泛型指针
func createTypePointerByAllocate() {
var ptr = UnsafeMutablePointer<Int>.allocate(capacity: 3)
ptr.initialize(to: 11)
ptr.successor().initialize(to: 22)
ptr.successor().successor().initialize(to: 33)

print(ptr.pointee)
print((ptr+1).pointee)
print((ptr+2).pointee)

print(ptr[0])
print(ptr[1])
print(ptr[2])
ptr.deinitialize(count: 3)
ptr.deallocate()
}

///指针之间的切换
func convertPointer() {
var ptr = UnsafeMutableRawPointer.allocate(byteCount: 16, alignment: 1)
ptr.assumingMemoryBound(to: Int.self).pointee = 11
(ptr+8).assumingMemoryBound(to: Int.self).pointee = 22

print(unsafeBitCast(ptr, to: UnsafePointer<Int>.self).pointee) //11
print(unsafeBitCast(ptr+8, to: UnsafePointer<Double>.self).pointee)
ptr.deallocate()
}