我正在参加「启航计划」

主要内容:

  1. 枚举的底层结构体认识
  2. rawValue的完成原理
  3. init的完成原理
  4. 形式匹配进程
  5. 内存巨细剖析
  6. Swift和OC混编

1. 枚举的底层结构体认识

1.1 原始值

代码:

/*
 1、原始值
 */
enum Week: String{
    case MON = "MON"
    case TUE = "TUE"
    case WED = "WED"
    case THU = "THU"
    case FRI = "FRI"
    case SAT = "SAT"
    case SUN = "SUN"
}

SIL:

//只需原始值
//主动生成init?初始化办法,已经rawValue核算特点
enum Week : String {
  case MON
  case TUE
  case WED
  case THU
  case FRI
  case SAT
  case SUN
  //参数是rawValue
  init?(rawValue: String)
  //RawValue类型也便是String
  typealias RawValue = String
  //rawValue只读核算特点
  var rawValue: String { get }
}

阐明:

1.2 相关值

代码:

SIL:

Swift底层探索(九)Swift枚举的底层源码探索

阐明:

  • 当运用了相关值后,就没有RawValue了,主要是由于case能够用一组值来表明,而rawValue是单个的值
  • 留意只需枚举中存在一个相关值,那么这个枚举就不存在rawValue了。(后边好好剖析一下)

2. rawValue的完成原理

代码:

/*
 3、rawValue的完成原理
*/
enum Week: String{
    case MON = "MON"
    case TUE = "TUE"
    case WED = "WED"
    case THU = "THU"
    case FRI = "FRI"
    case SAT = "SAT"
    case SUN = "SUN"
}
let mon = Week.MON.rawValue

SIL的main办法:

Swift底层探索(九)Swift枚举的底层源码探索

阐明:

  1. 拿到枚举
  2. 获取到getter办法
  3. apply调用办法,而且传入枚举
  4. 赋值给全局变量
  5. 因而这里最重要的便是getter办法传入枚举获取到rawValue值

SIL的getter办法:

Swift底层探索(九)Swift枚举的底层源码探索

阐明:

  1. 经过传入的枚举经过匹配枚举项找到对应的分支
  2. 在不同的分支上构建对应的字符串
  3. 跳转到b8回来这个字符串,而这个字符串便是rawValue拿到的值
  4. 构建字符串本质便是到底层获取到字符串

在底层存储的字符串:

Swift底层探索(九)Swift枚举的底层源码探索

3. init的完成原理

3.1 调用机遇

代码:

/*
 4、init办法
 */
enum Week: String{
    case MON = "MON"
    case TUE = "TUE"
    case WED = "WED"
    case THU = "THU"
    case FRI = "FRI"
    case SAT = "SAT"
    case SUN = "SUN"
}
print(Week.MON.rawValue)
let w = Week.MON.rawValue
Week.init(rawValue: "MON")
print("end")

界说一个符号断点

Swift底层探索(九)Swift枚举的底层源码探索

阐明:

  • 能够看到只需经过Init办法进行调用时才会履行init办法
  • 直接获取ravwValue,是不会履行init流程的

3.2 底层剖析

代码:

Swift底层探索(九)Swift枚举的底层源码探索

SIL的init办法:

Swift底层探索(九)Swift枚举的底层源码探索

阐明:

  1. 在init办法中是将一切enum的字符串从Mach-O文件中取出,顺次放入数组中
  2. 放完后,然后调用_findStringSwitchCase办法进行匹配
  3. 1、先创立一个数组
  4. 2、将第一个值存储到数组中
  5. 3、核算得到第二个值的地址,将第二个值存储到数组中
  6. 4、顺次履行,把一切元素放到数组中

_findStringSwitchCase: 经过init办法来创立枚举对象时,需求判别枚举中是否存在该元素

Swift底层探索(九)Swift枚举的底层源码探索

Swift底层探索(九)Swift枚举的底层源码探索

阐明:

  • 1、遍历数组,假如匹配则回来对应的index
  • 2、假如不匹配,则回来-1

匹配之后的判别:

Swift底层探索(九)Swift枚举的底层源码探索

Swift底层探索(九)Swift枚举的底层源码探索

阐明:

  • 假如没有匹配成功,则构建一个.none类型的Optional,表明nil
  • 假如匹配成功,则构建一个.some类型的Optional,表明有值

4. 形式匹配进程

4.1 原始值

代码:

/*
 5、形式匹配,原始值
 */
/*
enum Week: String{
    case MON
    case TUE
    case WED
    case THU
    case FRI
    case SAT
    case SUN
}
var current: Week?
switch current {
    case .MON:print(Week.MON.rawValue)
    case .TUE:print(Week.MON.rawValue)
    case .WED:print(Week.MON.rawValue)
    default:print("unknow day")
}

SIL:

Swift底层探索(九)Swift枚举的底层源码探索

阐明:

  • 其内部是将nil放入current全局变量,然后匹配case,做对应的代码跳转

4.2 相关值

代码:

/*
 6、形式匹配,相关值
 */
enum Shape{
    case circle(radius: Double)
    case rectangle(width: Int, height: Int)
}
let shape = Shape.circle(radius: 10)
switch shape{
    case .circle(let radius):
        print("circle radius: \(radius)")
    case .rectangle(let width, var height):
        height += 1
        print("rectangle width: \(width) height: \(height)")
}

SIL:

Swift底层探索(九)Swift枚举的底层源码探索

阐明:

  1. 首要构建一个相关值的元组
  2. 根据当时case枚举值,匹配对应的case,并跳转
  3. 取出元组中的值,将其赋值给匹配case中的参数

5. 内存巨细剖析

5.1 原始值

代码:

/*
 7、内存巨细
 */
//只需一个成员
enum NoMean1{
    case a
}
print(MemoryLayout<NoMean1>.size)
print(MemoryLayout<NoMean1>.stride)
//多个成员
enum NoMean2{
    case a
    case b
}
print(MemoryLayout<NoMean2>.size)
print(MemoryLayout<NoMean2>.stride)

成果:

Swift底层探索(九)Swift枚举的底层源码探索

阐明:

  • 一个成员,不占用内存
  • 多个成员时,会占用一个字节巨细的内存

LLDB检查:

enumNoMean{
casea
caseb
casec
cased
}
vartmp=NoMean.a
vartmp1=NoMean.b
vartmp2=NoMean.c
vartmp3=NoMean.d

Swift底层探索(九)Swift枚举的底层源码探索

阐明:

  • 在枚举内存中存储的是序号,用来符号枚举成员(从0开端,一共4个序号)
  • case是UInt8,即1字节(8位),最大能够存储255
  • 假如超过了255,会主动从UInt8 -> UInt16 -> UInt32 -> UInt64 …(再看下教案)

总结:

  1. 假如enum中有原始值,即rawValue,其巨细取决于case的多少,假如没有超过UInt8即255,则便是1字节存储case
  2. 只需一个成员时,此刻不需求运用序号符号,因而枚举内存没有存储任何值
  3. 假如有多个成员,就需求运用需求符号成员,枚举就会占用1个字节巨细的内存

5.2 相关值

代码:

/*
 8、内存巨细:相关值
 */
enum Shape{
    case circle(radius: Double)
    case rectangle(width: Double, height: Double)
}
print(MemoryLayout<Shape>.size)
print(MemoryLayout<Shape>.stride)

成果:

Swift底层探索(九)Swift枚举的底层源码探索

LLDB:

Swift底层探索(九)Swift枚举的底层源码探索

阐明:

  1. num有相关值时,相关值的巨细 取 对应枚举相关值 最大的
  2. circle中相关值巨细是8,而rectangle中相关值巨细是16,所以取16
  3. 可是由于有多个成员,所以还需求一个字节巨细用来存储序号,所以便是17个
  4. 分配内存时遵从字节对齐准则,因而分配了24个字节的空间

5.3 递归枚举

运用关键字indirect就能够完成枚举的递归 ,能够润饰在enum前,也能够润饰在枚举内的成员前

5.3.1 运用:

具体的能够看另一篇博客

/*
 9、递归枚举
 */
//用枚举表明链表结构
//放在case前
enum List1<T>{
    case end
    //表明case使是引用来存储
    indirect case node(T, next: List1<T>)
}
//放在enum前
indirect enum List2<T>{
    case end
    case node(T, next: List2<T>)
}

5.3.2 检查巨细:

代码:

Swift底层探索(九)Swift枚举的底层源码探索

阐明:

  • 不管是String类型仍是Int类型,此刻都是8个字节,而且占用内存巨细便是8个字节
  • 此刻枚举包括两个成员,可是end是原始值,所以内存巨细取决于node的成员的巨细

5.3.3 LLDB检查内存结构

1、先检查node

代码:

enumList<T>{
caseend
indirectcasenode(T,next:List<T>)
}
varnode=List<Int>.node(10,next:List<Int>.end)
print(MemoryLayout.size(ofValue:node))
print(MemoryLayout.stride(ofValue:node))

LLDB:

Swift底层探索(九)Swift枚举的底层源码探索

阐明:

  • 假如枚举被indirect润饰,编译器就不再核算这个枚举的巨细
  • 在堆中开辟了一个空间,将这个地址放到枚举中
  • 因而递归的时候,是无法确认实践巨细的,只能经过运转中递归的判别条件来决议

2、再检查end

代码:

LLDB:

Swift底层探索(九)Swift枚举的底层源码探索

阐明:

  • 假如存储的是end,那么他就和普通的枚举一样
  • 只不过这里存储的值是的当时序号的2倍

6. Swift和OC混编

上面咱们能够知道swift有很强大的功能,能够增加办法特点,还有相关值原始值,而且成员有多种类型,而OC的枚举只能是Int类型,局限性比较大,所以假如要进行混编,就需求进行必定的特别考虑

6.1 OC调用Swift

代码:

<!--swift中界说-->
@objcenumWeek:Int{
caseMON,TUE,WED,THU,FRI,SAT,SUN
}
<!--OC运用-->
-(void)test{
Weekmon=WeekMON;
}

阐明:

  • 用@objc进行润饰,露出给OC
  • 有必要是Int类型

OC如何访问swift中String类型的enum:

@objcenumWeek:Int{
caseMON,TUE,WED

varval:String?{
switchself{
case.MON:
return"MON"
case.TUE:
return"TUE"
case.WED:
return"WED"
default:
returnnil
}
}
}
<!--OC中运用-->
Weekmon=WeekMON;
<!--swift中运用-->
letWeek=Week.MON.val

阐明:

  • swift中的enum尽量声明成Int整型
  • 然后OC调用时,运用的是Int整型的
  • enum在声明一个变量/办法,用于回来固定的字符串,用于在swift中运用

6.2 Swift调用OC

6.3.1 方法一:typedef enum

OC代码:

Swift代码:

阐明:

  1. OC的这种声明方法,会转化成Swift的结构体
  2. 之后就需求留意运用时的样式
  3. 并遵从了两个协议:Equatable 和 RawRepresentable

6.3.2 方法二:typedef NS_ENUM

OC代码:

Swift代码:

阐明:

  • OC指定类型的方法,Swift也会指定类型

6.3.3 方法三:NS_ENUM

OC代码:

Swift代码:

阐明:

  • 主动转化

7、总结