Swift

作者: 想成为大牛的程旭元 | 来源:发表于2021-10-31 15:41 被阅读0次
sizeof 不是函数,在汇编里面,没有掉callq
类型分类.png

@inline

内联-1.png 内联-2.png 内联-3.png

@inout

inout-1.png
  • 传的是内存地址(引用传递)
inout-3.png 可选绑定-1.png

空合并运算符 ??

空运算符-1.png 空运算符-2.png

guard语句

guard-1.png

隐式解包

隐式解包-1.png

enum

enum  TestEnum{
    case test1(Int,Int,Int)
    case test2(Int,Int)
    case test3(Int)
    case test4(Bool)
    case test5
}
var e = TestEnum(10,20,30);
  • 上面最后一句代码,就是把10、20、30放在e的内存地址上
mov $0xa, 0x4eb(%rip), //  rip存储的地址就是 0x4bdf
leaq 0x4bdf(%rip) 
  • rip存储的是指令的地址
  • CPU要执 行的下一条指令地址就存储在rip中

结构体和类区别

  • 结构体(值类型,枚举也是值类型),类引用类型
class Size{
 var width = 1;
  var height = 2;
}
var size = Size();
size是指针,放在内存的栈空间,占8个字节
size指针变量地址,在堆空间,Size对象的内存地址 ,占32个字节,看堆空间的绿色一共4个,4*8=32 
1.png

图中蓝色和绿色 内存数据那块,一格表示8个字节。

对象堆空间申请内存过程

对象堆空间申请内存过程.png 值类型.png

闭包

闭包.png
  • fn里面前8个字节存储的是getFn里面plus函数地址,后面8个字节存储的是堆空间的地址。

  • fn(1),1这个参数在汇编里面是通过rdi寄存器传进来的,一直存储在rdi寄存器里,到了plus函数里,直接从rdi寄存器拿出来就行。

  • num是存储在堆空间里面的,向堆空间申请了24个字节,前8个字节跟类一样,指向类型信息,再8个字节,放引用计数,最后8个字节,放num。

  • plus里面可以拿到堆空间地址,再调用plus函数的时候,传了2个参数,一个是参数i,一个是堆空间地址值

  • num是在调用getFn函数时,函数返回时,把num传到堆空间里的

  • 调用一次getFn函数,就会开辟一次堆空间

闭包2.png
  • plus没有捕获外部变量
  • 上图中fn1占16个字节,前面8个字节,放的是plus地址,后面8个字节是0
闭包3.png
  • fn1num在堆空间里的值是14,汇编看是捕获了2次,第一次是11,第二次是14。

全局变量

全局变量.png
  • 上面代码fn1是函数plus地址,前面8个字节是plus地址,后8个字节是0
  • 全局变量,不会发生捕获,不算闭包

闭包:函数+捕获的变量。

闭包-4.png
  • 上面代码调用一个getFns只会开辟一次堆空间,这个意思是num1num2各自只开辟一次堆空间,不是说调用plus的时候开辟一次,调用minus的时候开辟一次。

  • num1只alloc了一次,开辟了一次堆空间,前8个字节存储类型,中间8个字节存储引用计数,最后8个字节存储num1

  • num2也只alloc了一次,开辟了一次堆空间,前8个字节存储类型,中间8个字节存储引用计数,最后8个字节存储num2

  • num2num1是分开来存储的,

自动闭包
自动闭包.png ??运算符.png
属性
属性-1.png 存储属性.png

计算属性

计算属性.png
枚举
枚举.png

枚举变量占一个字节来存储对应的case。
那么原始值存储在哪?

  • 原始值不存储,是个只读的计算属性。


    枚举的rawValue.png

延迟存储属性lazy

lazy.png lazy-2.png 延迟属性的特点-1.png

属性观察器

属性观察器-1.png 属性观察器-2.png 全局变量和局部变量.png
inout在各个属性之间的地址取值
  • 引用传递


    代码-1.png
代码-2.png 代码-3.png
存储属性
test(&s.width)
  • 上面代码width是存储属性,有地址值,存储在s里面,所以test函数结束到的是s的地址值,又因为width是s的第一个属性,所以传s的地址值是一样的。

计算属性

test(&s.girth)
  • 上面girth是计算属性,计算属性本身没有地址值,首先,调用get方法拿到返回值,放到局部变量里面去,第二,把局部变量的地址值传给test函数,test函数把20赋值给局部变量最后局部变量赋值给girth,调用set函数`

属性观察器

test(&s.side)
  • side是属性观察器,先调用test函数,在test函数里面进行赋值的时候,会调用set函数,在set函数里会触发willSetdidSet函数。
    传值过程
    1. 调用test之前,先把side里面的内容赋值给局部变量
    1. 局部变量的地址值赋值给test函数test函数修改完局部变量的值
  • 局部变量的值,传递到set方法里,
  • willSet的newValue就是局部变量的值
  • 局部变量的值放到set的存储属性里面
  • 赋值完之后调用didSet
  • 真正修改side的值,是在test函数调用完毕之后,才去改的。
  • test函数是一个独立的函数,只接收一个地址值,修改这个地址值里面的数据。
inout总结
inout总结.png
类型属性
类型属性.png 类型属性-count.png
  • count只有一份内存
类型属性细节
类型属性细节.png

单例模式

单例.png
  • 类型属性的最好应用
class Car{
    static var count = 1;
}
Car.count = 11;
  • count是lazy属性,是在第一次调用的时候才初始化,初始化的时候调用了swift_once函数,swift_once函数里调用了dispatch_once函数,就等价于
  • count是存储类属性,线程安全,调用dispatch_once
dispatch_once({
     Car.count = 1;
})
  • 存储类属性是全局变量,只不过编译器限制了他的访问量

方法

方法.png self.png

mutating

mutating.png

discardableResult

discardableResult.png

下标--subscript

下标.png 下标-2.png

下标细节

细节-1.png 细节-2.png

结构体和类作为下标返回值区别

区别.png
  • struct Point
pm[0].x = 11;
等价于
pm[0].x = Point(x:11,y:pm[0].y);

pm[0].x = 掉的是set方法

  • class Point
pm[0].x = 11;
  • 相当于掉了下标里面的get方法,把point返回,point是一个指针变量,你可以拿到指针变量,去修改这个地址的值了。所以不报错
  • 要是换成structPoint,下标函数里没有set方法pm[0].x = 11;就会报错,因为值类型,内容是拷贝赋值给一个新的临时变量,改不了旧的。

接收多个参数的下标

接收多个参数的下标.png 接收多个参数的下标-1.png
重写类型方法、下标
重写类型方法、下标.png 1.png 2.png
  • SubCircle里面依然有8个字节来存储radius属性,要不然赋值没地方存。
var subCircle = SubCircle()
subCircle.radius = 10;
打印的结果为
SubCircle getRaius
Circle getRaius
SubCircle setRaius

从父类继承过来的存储属性,都是有存储空间的。不管要不要写成计算属性

  • SubCircle里radius属性,get方法里return super.raius 改成raius,直接死循环了,因为不知道拿谁的raius
  • 计算属性是有get、set方法
重写实例属性
重写属性.png
重写类型属性
重写类型属性.png
  • 存储属性本身不让被class修饰


    不让class修饰.png

属性观察器

属性观察器.png
  • 父类有属性观察器


    父类有属性观察器.png
  • 子类可以给父类的计算属性添加属性观察器


    计算属性.png

打印的结果为 Circle getRadius 是 oldValue,因为在赋值之前,要拿到对应的值

final

final.png

结构体不存在继承,所以不存在重写,在编译阶段就把函数地址、属性写死了。

初始化器

初始化器.png
  • 调用规则 从它的直系父类调用指定的初始化器,这是为了保证父类初始化里面一系列操作可以正常执行。

初始化器相互调用

初始化器相互调用.png

2段式初始化

2段式初始化.png
安全检查
安全检查.png 1.png 2.png

重写

  • 便捷初始化器不能被子类调用。


    重写.png
重写第一条的解释.png 重写第二条的解释-1.png 重写第二条的解释-2.png
  • 子类无法去覆盖Person类的 convenience init()方法的,convenience init()只能在Person类里其他调用,子类掉用不了,不算重写
自动继承
自动继承.png 第一条规则的解释.png 第二条规则的解释.png

required

required.png

属性观察器

属性观察器.png

可失败初始化器

可失败初始化器.png

反初始化器 deinit

deinit.png

可选链 Opitional Chaining

可选链 .png

协议 Protocol

协议1.png 协议2.png

static class

static-class.png
  • static不可以子类重写,class子类可以重写
    mutating
    mutating.png
    init
    init.png
init-2.png
  • 上面的Student 里面required 来自协议,override来自父类

init 、init?、init!

init 、init?、init!.png 协议2.png 协议3.png

CaseIterable

CaseIterable.png
  • allCases 返回一个数组

CustomStringConvertible CustomDebugStringConvertible

  • CustomStringConvertible里面有description
    CustomStringConvertible.png
CustomDebugStringConvertible.png

Any AnyObject

Any AnyObject.png

**is as? as! as **


is as? as! as.png

X.self、X.Type、AnyClass

  • X是类
Person.self 放元类地址
跟person对象的前8个字节存储的地址一样
1.png

元类型的使用

元类型的使用.png

继承

继承.png
继承2.png

Self

  • 跟OC里面的instanceType差不多


    Self.png
Self-2.png 4句都调用了init.png
error 错误类型
开发错误.png
自定义错误
自定义错误-1.png
do-catch
do-catch.png

处理Error

处理Error.png 代码.png

try? try!

try? try!.png
  • try?异常是nil,不异常是Option值
  • try!告诉系统一定没错, 如果发生错误, 程序会崩溃. 不推荐使用
  • 首先要明白抛出异常后异常的运动:异常被抛出后,中断整个处理,异常不断向外层(范围)传递,直到遇到catch代码块群,会与catch代码块的条件进行匹配,匹配符合则进入此代码块处理。如果遇到没有条件的catch{}那么直接在这个代码里处理。如果抛出的异常一直到最外层仍没有被catch{}处理,那么程序会卡住(后面的处理全部中断)

rethrows

rethrows.png

defer

defer.png
  • 一段代码,不管你是正常执行完,还是直接崩溃跳出代码,之后,需要做的操作,比如读数据库,不管是正常读完,关闭数据库,还是SQL写的不对直接崩溃,退出,关闭数据库,在defer执行。
泛型
泛型-1.png

泛型属性

泛型属性.png
关联类型(Associated Type)
关联类型.png
类型约束
类型约束-1.png 类型约束-2.png equal.png
  • equal函数的意思是:S1、S2必须遵守Stackable协议,S1、S2的关联对象必须一致,S1的关联对象Element必须遵守Hashable协议,多要求的话必须在返回值后面加where关键字

协议类型的注意点

协议类型的注意点.png

泛型解决一

解决一.png
泛型解决二---不透明类型(Opaque Type)
  • 不透明类型使用场景:返回一个遵守某种协议的对象,拿给别人用,但是又不告诉别人具体是那种类型,而且别人拿到的接口仅仅是协议里面的接口,用不透明类型
    解决2-some.png
some-2.png

assert断言

assert.png
  • do-catch 不可以捕获

fatalError

fatalError.png
访问控制Access Control
  • 实体 ,下面name就是实体
class Person{
    public var name = ""
}
模块.png
  • open 其他模块可以继承、重写
open class Person{
    open var name = ""
}
  • internal 只能在当前实体里使用,不能当做第三方库被别人使用
    internal class Person{
    var name = ""
    }
  • fileprivate 加入在main.swift里定义了一个 Person类,那么Person类只能在main.swift文件中使用
  • private, 下面name只能在Person{}这个大括号里使用
class Person{
    private var name = ""
}
访问控制.png
  • 绝大部分实体都是internal级别
访问级别的访问准则
1.png 变量类型>= 变量的访问级别.png 父类型>=typealias.png 原始值类型.png 定义A类型.png
元组类型
元组类型.png
泛型类型
泛型类型.png
成员、嵌套类型
成员、嵌套类型.png 1.png
  • 上面代码在全局作用域中,2个作用域类型是一样的。private和fileprivate是一样的

  • private是在定义的文件里访问,在全局作用域,就是当前的文件里都能访问


    2.png
  • private和fileprivate是在Test里面,不符合父类高于子类的规定

4.png
  • 上面Dog是private,但是在全局作用域里,private相当于fileprivate,那么age也是fileprivate

直接在全局作用域下定义的private等价于fileprivate

重点

5.png
  • 首先上面代码是运行成功的。

问Person里面的dog.run()为啥不报错

  • 因为Dog和Person都是private修饰,意味着Dog是在Test类里可以使用,那么Dog的run()函数和age都是可以在Test类里使用的
6.png
  • 上面代码 age加了一个private,那么age只能在定义它的实体里使用,也就是在Dog里面使用,外部使用不到。
getter setter
getter setter.png
初始化器
初始化器.png 结构体.png
  • 一旦上面的y是private,那么Point(y:10)会报错,因为带y的初始化器只能在Point里面使用
枚举
枚举.png
协议
协议.png
  • 针对于最后一条理解:协议有定义协议的访问级别,有实现协议里面方法的访问级别,当实现协议里面方法的访问级别 >= 定义协议的访问级别,就好比下面代码
定义协议的访问级别  private
private protocol A{
      run()
}
class Person:A{
    实现协议的访问级别 public
    public run(){}
}
  • 上面代码不报错
扩展
扩展.png 扩展1.png
  • Person扩展里run()函数,在别的文件里不能被调用,只能在当前文件里调用,run()的访问级别是fileprivate
第四条解释.png
扩展2
扩展2.png
  • 上面3个扩展都是在同一个文件中,相当于所有代码都在同一个类中。
将方法赋值给var/let
struct Person {
  var age:Int
  func run(_ v:Int){ print ("func run",age,v)}
  static func run(_ v:Int){ print ("static func run",v)}
}
let fn1 = Person.run;
fn1(10);  //static func run 10

let fn2:(Int)->() = Person.run;
fn2(20);  //static func run 20

  • 上面代码,先传一个Person实例,在调用函数run
将方法赋值给var、let-4.png
  • 实例方法不用传Person实例

内存管理

内存管理.png 内存管理-2.png

自动释放池

自动释放池.png

循环引用

循环引用-1.png

闭包循环引用

闭包循环引用.png
  • p强引用闭包,闭包又强引用p
闭包循环引用.png 闭包循环引用2.png 闭包循环引用3.png
  • ()这个是马上调用,就把age值赋给了getAge,闭包表达式不存在了,闭包表达式self.age执行完生命周期就完了,相当于getAge本质是Int类型,getAge=self.age,所以这就不用写weak
@escaping
escaping.png
  • 如果是逃逸闭包必须写self,因为影响了self的生命周期。
逃逸闭包注意点2
逃逸闭包.png
do
do.png

内存访问冲突(Conflicting Access to Memory)

内存访问冲突.png 内存访问冲突-2.png

指针

指针.png
  • UnsafeMutablePointer是可以修改指针的值的
  • pointee是可以取出指针的值的,相当于C里面的*ptr
  • UnsafeMutableRawPointer不知道传递的指针是什么类型,所以storeBytes()里面的as类型
  • load里面的as也是类型,这个函数是取值。
代码-2.png
指针使用场景
指针使用场景-1.png

获得指向某个变量的指针

获得指向某个变量的指针.png

获得指向堆空间实例的指针

获得指向堆空间实例的指针.png
  • ptr存储的是堆空间的指针,不是person 的指针
  • UnsafeRawPointer就是把指针指向的地址取出来,这里ptr3拿到的是person的地址,heapPtr指向的是person指针指向的堆空间的地址。
获得person的指针.png
  • ptr存储的是person 的指针,unsafePointer传谁的值,返回谁的值,这里是person的值,返回person的值。

创建指针

创建指针.png 创建指针-2.png
  • malloc 开辟了16个字节的数据,然后 store前8个字节存11,接着store(offSet)偏移8个字节,存22。
  • advanced指针挪动8个字节,返回给你一个指针,是下一个8个字节的指针,2个指针之间相差8个字节。相当于 0x00 + 8个字节 = 0x08,返回给你。
  • 这里rawPointer不知道那个类型,所以得传类型。
创建指针-3.png
  • 这里指定了类型Int,这是capacity是容量,相当于开辟了2*8=16个字节。
创建指针-4.png
  • initalize是 初始化前8个字节,也就是0x000
  • successor指向的内存是 后8个字节,0x008
创建指针-5.png
  • 调用了 initalize
  • 最后 得掉deinitalize释放

指针转换

指针转换-1.png
  • ptr.assumingMemoryBound是把任意类型转一种类型
指针转换-2.png
  • 把ptr转成Int,就相当于把二进制数据给你,然后类型发生变化,原来是任意类型,现在是Int。

字面量(Literal)

字面量.png

字面量协议

字面量协议.png

字面量协议应用

字面量协议应用.png

模式匹配

通配符
通配符-规则.png 通配符--代码.png
标识符模式
标识符模式.png
值绑定模式
值绑定模式.png
元组模式
元组模式.png
枚举case模式
枚举case模式.png
可选模式
可选模式.png
  • ?非nil,非空值
类型转换模式
类型转换模式.png
自定义模式
自定义模式.png
自定义模式表达式
自定义模式表达式.png
where用处
where.png

OC到Swift

标记
标记.png
编译条件
编译条件--1.png 编译条件--2.png
系统版本检查
系统版本检查.png
API可用性
API可用性.png

Swift掉OC

Swift掉OC--1.png person.h.png person.m.png
Swift调用.png

Swift掉OC --- @silgen_name

Swift掉OC --- @silgen_name.png

OC掉Swift

OC掉Swift .png OC掉Swift -代码.png OC掉Swift-@objc.png

选择器Selector

选择器Selector.png

1. 为啥Swift暴露给OC的类最终要继承NSObject?

  • 这个类是在OC里面用,在OC里面用肯定得继承NSObject,得有isa指针,最终走objc_msgSend。
  • OC依赖与runtime,runtime要求类有isa指针,isa指针肯定继承NSObject。
    2. Swift类调用OC类里方法,是怎么调用的?反过来OC调用Swift又是怎么调用的?
  • 纯Swift掉方法是走虚表那套机制
Call 0x0xxxx 找这个地址
  • Swift掉OC的方法,还是走的objc_msgSend机制
  • OC掉Swift的方法,肯定也是走objc_msgSend,因为继承NSObject,然后依赖isa

下面的类里run怎么走?

Swift类.png
  • 还是走Swift虚表这套。在Swift里面,肯定走Swift里面的效果。

String

字符串一.png 插入和删除.png
  • endIndex指的是2后面的位置
  • startIndex指的是1的位置

Substring

Substring.png substring流程.png
  • string为Hello,world 然后Substring那你截取的话,Substring同样指向Hello,world的地址,2个公用一份地址,然后把数据返回,
  • 如果要把Substring类型转成string,那么就得吧Substring地址的数据深拷贝出一份,形成一个新的地址。
Substring.png

String相关协议

String相关协议.png 多行String.png
String和NSString
String和NSString.png
Swift和OC桥接转换表
Swift和OC桥接转换表.png
  • 上面的是直接as

只能被class继承的协议

协议--1.png 协议--2.png

dynamic

dynamic.png

KVC、KVO

KVC、KVO.png

block版本的KVO

block版本的KVO.png

关联对象

关联对象.png
  • Void是一个字节,不浪费内存

资源名管理

资源名管理-1.png 资源名管理-2.png

资源名管理的其他思路

资源名管理的其他思路.png

多线程开发---异步

多线程开发---异步.png

数组操作map

  • map会遍历元素,遍历一次,调用一次闭包,然后将元素传给你,操作,然后返回一个值,然后将返回值,放到一个新的数组,返回给你。

数组操作filter---过滤

  • filter会遍历元素,遍历一次,调用一次闭包,然后将元素传给你,操作,然后返回一个Bool值,如果返回为true,那么就返回到数组里面去

数组操作reduce

reduce.png
  • reduce函数,接收2个参数,第一个参数初始值,第二个参数闭包返回值为result,类型跟初始值一样。
  • 循环遍历数组中每个元素,第一次遍历,result为初始值,上面代码为0,element为1,2个数值相加,作为下次循环的result--1
  • 第二次循环,也就是2了,element为2,result为第一次循环的结果1,然后2者相加,结果result3,作为下次循环的结果,
  • 始终result作为下次循环的参数,和element相加,一直到循环结束,赋值给arr2
  • 这里就相当于0+1+2+3+4
截屏2021-10-25 下午2.13.47.png

map和flatmap区别

  • (1)flatMap返回后的数组中不存在nil,同时它会把Optional解包
let array = ["Apple", "Orange", "Puple", ""]

let arr1 = array.map { a -> Int? in
    let length = a.characters.count
    guard length > 0 else { return nil }
    return length  
}
arr1 // [{some 5}, {some 6}, {some 5}, nil]

let arr2 = array.flatMap { a-> Int? in
    let length = a.characters.count
    guard length > 0 else { return nil}
    return length    
}    
arr2 // [5, 6, 5]
  • (2)flatMap还能把数组中存有数组的数组(二维数组、N维数组)一同打开变成一个新的数组
let array = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]

let arr1 = array.map{ $0 }
arr1 // [[1, 2, 3], [4, 5, 6], [7, 8, 9]]

let arr2 = array.flatMap{ $0 }
arr2 // [1, 2, 3, 4, 5, 6, 7, 8, 9]
  • (3)flatMap也能把两个不同的数组合并成一个数组,这个合并的数组元素个数是前面两个数组元素个数的乘积
let fruits = ["Apple", "Orange", "Puple"]
let counts = [2, 3, 5]

let array = counts.flatMap { count in
    fruits.map ({ fruit in
         return fruit + "  \(count)"            
    })   
}
array // ["Apple 2", "Orange 2", "Puple 2", "Apple 3", "Orange 3", "Puple 3", "Apple 5", "Orange 5", "Puple 5"]
  • map:可以对数组中的每一个元素做一次处理

compactMap

  • 当闭包中的返回结果是可选的时候,使用compactMap代替flatMap,那么当闭包中的返回结果不是可选的时候,依然使用flatMap。
let arrayString = ["Ann", "Bob", "Tom", "Lily", "HanMeiMei", "Jerry"]
        let arrayInt = arrayString.compactMap { (str) -> Int? in
            return str.count
        }
        print("arrayInt: \(arrayInt)")
        // arrayInt: [3, 3, 3, 4, 9, 5]
        
        // 简化
        let arrayInt2 = arrayString.compactMap { $0.count }
        print("arrayInt2: \(arrayInt2)")
        // arrayInt2: [3, 3, 3, 4, 9, 5]
        
        let arrayI = arrayString.compactMap { $0.contains("i") ? $0 : nil }
        print("arrayI: \(arrayI)")
        // arrayI: ["Lily", "HanMeiMei"]
  • 结果可以看出,虽然闭包返回值是可选的,但是真正返回的结果中,并不是可选的,也会过滤掉nil的情况。

lazy的优化

lazy的优化.png lazy的优化-2.png
  • 看上面代码lazy在需要的时候才去进行操作,而且result[1]的时候,只是去map2这一个元素,当这数据量大的时候,可以使用lazy进行优化。

option的Map和flatMap区别

option的Map和flatMap区别.png
option的Map
  • 如果有值,那么取值以后进行map操作,操作再加一层Option,那么返回的是??这种格式
  • 如果没值,那么直接返回nil
    option的flatMap
  • 如果有值,那么取值以后进行flatMap操作,操作再加一层Option,那么返回的还是?这种格式

高阶函数

高阶函数.png

柯里化Currying

柯里化.png

函子(Functor)

函子-1.png 函子-2.png

适用函子(Applicative Functor)

适用函子--1.png 适用函子--2.png

单子(Monad)

单子.png
  • F是类型

面向协议编程

面向协议编程-1.png

OOP

OOP.png

OOP不足

OOP不足.png

POP解决方案上面不足

POP解决方案-1.png

POP注意点

POP注意点.png

计算属性

  • 内存里没有一个存储属性来指向他
  • 本质是方法

利用协议实现前缀

利用协议实现前缀-1.png Base代码.png

Base协议

  • Base后面继承一个协议


    Base协议.png

利用协议判断类型

利用协议判断类型--1.png

响应式编程

响应式编程.png

RXSwift

RXSwift.png

RXSwift核心角色

RXSwift核心角色--总览.png
创建、订阅Observable1
创建、订阅Observable1.png
Observable.just(1111)
等同于
// 发送消息
observable.onNext(22222)

// 发送多个消息,然后接收方 接收3次
Observable.of([1,2,3])
//  发送多个消息,然后接收方 接收3次
Observable.from([1,2,3])

Observable.of和Observable.from打印结果.png
创建、订阅Observable2
创建、订阅Observable2.png
  • seconds(3) 3秒之后再发消息

  • period: .seconds(1) 周期,3秒之后,每隔1秒再发消息

  • scheduler:MainScheduler.instance 主线程

  • 上面就是个定时器

Disposables

Disposables.png
  • DisposeBag() 是上面bag对象
  • self.rx.deallocated 跟随着self的生命周期,当self销毁的时候,自动释放。
  • bind把数据绑定到对应的控件上去

创建Observer

创建Observer.png

扩展Binder属性

  • Binder是Observe


    扩展Binder属性.png

RXSwift的状态监听1

RXSwift的状态监听1.png

RXSwift的状态监听2

RXSwift的状态监听2.png

即是Obeservale又是Obeserver

1.png
  • ControlProperty即是Obeservale又是Obeserver
即是Obeservale又是Obeserver.png

Swift源码

Swift源码简介-1.png
Swift源码地址.png

Array源码分析

map源码
map源码.png
  • interator迭代器,interator.next()迭代器拿到一个元素,传到transform()函数里面,并且把transform()的返回值放到result里。
  • 最终把Array(result)返回
filter源码
filter源码.png
  • 调用_filter函数
  • isInclude()函数,就是filter穿进去的判断函数,
  • interator迭代器,interator.next()迭代器拿到一个元素,
  • 把这个元素传给isInclude()函数,返回true,将这个元素拼接到result数组里面
  • 最终把Array(result)返回

flatMap compactMap reduce

flatMap

flatMap.png
  • 定义一个result数组
  • 循环遍历,拿到element,传给transform函数
  • 把结果添加到result
  • 返回result

compactMap

compactMap.png
  • compactMap函数_compactMap

  • _compactMap函数,循环遍历元素,然后判断元素不为nil,添加到result

  • 返回result

  • `compactMap函数 去掉数组中的nil值

Substring

Substring--1.png Substring--2.png
  • base函数,拿到之前指向的字符串,是因为返回_slice.base,就是外面传进来的字符串
  • _slice.base里bounds记录着字符串是那段区域
Substring.png

Substring的append方法

Substring的append方法.png
  • var string = String(self)append函数里substring把自己穿进去,创建一个新的string,
  • 然后创建一个新的地址,把原来内容拷贝进去,
  • 把外面传进来的内容,拼接到新的string里
  • 然后再拿新的string创建一个新的Substring

Opition的map和flatMap源码

flatMap

flatMap.png
  • 枚举值,先判断自己是否有值,如果是.none,就是没值,返回.none
  • 如果有值 取出来赋值给y,调用transform函数
  • 返回transform函数的值

map

map.png
  • 枚举值,先判断自己是否有值,如果是.none,就是没值,返回.none
  • 如果有值 取出来赋值给y,调用transform函数,拿到返回值,
  • 再包装一层.some(返回值) 返回回去

区别

  • 就是在有值,返回的时候,flatMap是直接返回transform函数的值
  • map是包装一层transform函数的值,.some(transform(y));

Opition的==号

==代码1.png
  • ==左边有值,右边为nil

    ==代码2.png
  • 上述代码可以看出rhs可以为nil,因为继承的协议可以初始化为nil

var age :Int? = 10
print(age == nil)

会触发上面的==代码

  • 判断lhs是不是为nil,如果是nil返回true,如果有值返回false
var age :Int? = 10
print(nil == age)

会触发下面的代码

  • 左边为nil,右边有值


    ==代码3.png

==比较2个正常的值

==比较2个有值.png
var age1 :Int??? = 10
var age2 :Int? = 10
var age3 :Int = 10

print(age1 == age3)
print(age1 == age2)
print(age2 == age3)
  • 先拿到左右2边的值,左右2遍的值能解包,就取值,返回 左边值 == 右边值这个判断
  • 如果2个值都是nil,返回true
  • 默认返回为false

Opition的??号 源码

var age1 :Int? = 10
var age2:Int? = 10
print(age1 ?? age2)
??代码1.png ??代码2.png
  • ??一个返回值是T,一个是T?
  • 都先判断左边的值是否有值,拿到左边的值先解包,有值都返回value,如果返回是T?,不代表返回的就是可选类型。
  • 左边有值,肯定得解包一次
  • 没值,返回,调用右边的defaultValue闭包

Metadata分析

class Person{}
var person  =  Person()

  • person指向堆空间,前8个字节指向Metadata
  • 中间8个字节是引用计数
  • 在后面就是属性变量、函数等等

Metadata

  • Metadata分类型的,struct、protocol、class的Metadata不一样。
    网址.png
Metadata--类型.png

ClassMetadata

class-metadata结构.png
  • kind 前8个字节,类型,是枚举、类、struct
  • superclass再8个字节

反射

反射.png

关于Sting的考虑

关于Sting的考虑.png
var str1 = "0123456789"
  • str1在内存里占16个字节,内存里存储的是ASCii码,
str1内存打印结果.png
  • 上面是小端模式读取内存的结果,小端模式是37在前,
  • 0对应的ASCII码值是30,那么9对应的值是39
  • 上面str1里面存储的值就是"0123456789"
  • 0xe9a就是长度(10个字符,0~9不就是10个字符吗),a最大是f,,e是类型,表示直接存储内容,
  • a最大是f,刚好填满15个字节,
    存储了15个字节.png
  • 上面的str1,直接将内容放到了内存中,类似于OC的target point,直接将内容放到了内存中
var str2 = '01234567890ABCDEF'
打印地址.png
  • 16个字符
  • 汇编调用函数,返回值如果大于8个字节,放到rax和rdx
  • 上面的代码会调用Swift.init初始化器,传了2个参数,一个是字符串的长度,一个是字符串的真实地址,会拿15跟字符串的长度进行比较,小于15直接存储在字符串的真实地址里,
  • 大于15,会把真实地址放到str2的后8个字节,因为str2是全局代码,这8个字节存储在TEXT段的Cstring段,常量区
字符串真实地址 + 0x7fffffffffffffe0 = 0x800000010000a790
也可以
字符串真实地址 + 0x20 = 0x800000010000a790
  • 读取地址,拿到对应的值


    真实地址.png

字符串拼接 append

  • 如果拼接完字符长度不够15,那么还是存储在内存中
  • 如果超过15,那么重新指向一块内存,那么就需要堆空间

从编码到启动App

从编码到启动App.png

字符串拼接

字符串拼接.png
  • 看打印的地址,2个前8个字节,0xd000000010,0xf00000011,这里面11和10 ,分别是字符串的长度,d0表示在常量区,
  • f0之后会调用String.init*()->malloc,重新分配地址
  • str2后8个字节放的是,append之前的地址值,这个地址值+0x20就是堆空间的地址值,
str2 append之前后8个字节地址值 + 0x20 = 拼接后字符串地址值
  • 0x2032个字节,放的是堆空间信息。
字符串探讨.png

dyld_stud_bind 符号绑定

App加载流程.png mac-o加载.png
var str1 = "0123456789"

str1.append('ABCDEF');
append调用可String.init()函数。
  • 底层调用了String.init()函数动态库,程序运行中才会把动态库载入进去,String.init()的地址,是程序启动之后才知道的,
  • 编译的时候。str1的地址是临时的,占位地址
  • 然后会执行0x1000a3ce jump *0x2c4c(%rip),0x2c4c就是一个地址
rip = 0x1000a3ce + 6 = 0x100000A3D4  
0x100000A3D4 + 0x2c4c = 0x100000D020
  • 取出 0x100000D020的地址,然后jump取出的值,直接拿D020,去mac-o里面查地址,
    mac-o文件的地址.png
取出的地址:0x0100000A5F4
  • jump跳到0x0100000A5F4后,一直进,会来到dyld_stud_bind
  • 绑定完了只会,再调到真正的String.init()函数地址

Array 值类型

var arr = [1,2,3,4]
  • arr占用了8个字节,是一个指针,指向堆空间。


    array地址打印.png
  • 抛过前4个指针(32个字节),从第5个指针看,是1

Array的结构图.png
  • 上面的容量,初始化的时候,是一定的,当元素数量超过容量的值的时候,容量会翻倍扩容(舍弃一部分空间,重新开辟一段新的内存)

可选项本质

enum.png
var age:Int? = 10
switch age {
    case let v:{
        print(v)
}
  • 上面代码相当于直接把let v = age,直接把age无条件赋值给了v。不管是不是nil。

溢出运算符(OverFlow Operator)

var v1 =   UInt8.max;
v2 = v1.&+1;  
print(v2)  // 0


var v1 =   UInt8.min;
v2 = v1.&-1;  
print(v2)  // 255


var v1 =   UInt8.max;
v2 = v1.&*2;    == 255 &+255 

// 因为 255 &+1 = 0    255 &+2 = 1    ... 所以为254
print(v2)  // 254 
  • 取值范围是 0~255 ,换算成数组长度的话就是 count = 256,如果 255 + 1 = 256 - count = 0。
    溢出运算符-1.png
溢出运算符-2.png

运算符重载(Operator Overload)

运算符重载.png
  • prefix -前置
++a;
  • postfix -前置
a++;

Equatable

Equatable.png
  • Equatable 继承这个协议,实现static func == () ->Bool函数
func equals <T:Equatable> (_ t1:T, _t2:T) ->Bool{ t1 == t2}
  • <T:Equatable>类型为T,都继承了Equatable协议

Comparable

Comparable--代码1.png Comparable--代码2.png

自定义运算符(Custom Operator)

自定义运算符-说明.png
  • assugnment:true,可选链中拥有赋值一样的优先级
class Person{
      var age = 0
}
func getAge() -> Int{ 10 }
var p:Person = Person();
p?.age = getAge()

赋值优先级,上面代码 p? 要是p有值,getAge()计算出结果,才会赋值给age,如果p为nil,那么就不会赋值

associativity:结合性,

  • left 是从左到右计算
  • right 是从右到左计算
  • none 不允许出现2个以上的运算,下面代码把left改成none会直接报错。不知道该怎么计算。
associativity.png

扩展(Extension)

扩展.png
  • 存储属性、继承增加内存结构,所以扩展不能添加这些。

协议、初始化器

协议、初始化器.png

上面最后一条解释 ---- 类实现required

上面最后一条解释.png

协议

协议1.png 协议2.png
  • 如果类里函数名称和协议里函数名称 一样,调用的时候,优先调用类里的函数。

泛型

泛型.png

相关文章

网友评论

      本文标题:Swift

      本文链接:https://www.haomeiwen.com/subject/tqgggltx.html