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