Swift底層原理探索2----枚舉

枚舉的基本你用法

enum Direction_1 {
    case north, south, east, west
}

enum Direction {
    case north
    case south
    case east
    case west
}
var dir = Direction.west
dir = Direction.east
dir = .north
print(dir)

switch dir {
case .north:
    print("north")
case .south:
    print("south")
case .east:
    print("east")
case .west:
    print("west")
}


關(guān)聯(lián)值(Associated Values)

關(guān)聯(lián)值是直接存在枚舉變量的內(nèi)存里面的,這點(diǎn)要牢記,對(duì)于一個(gè)有固定取值范圍的變量,設(shè)計(jì)成枚舉比較合適

enum Score {
    case points(Int)
    case grade(Character)
}
var score = Score.points(96)
score = .grade("A")

switch score {
case let .points(i):
    print(i, "points")
case let .grade(i):
    print("grade", i)
} // grade A

enum Date {
    case digit(year: Int, month: Int, day: Int)
    case string(String)
}
var date = Date.digit(year: 2020, month: 02, day: 29)
date = .string("2020-02-29")
switch date {
case let .digit(year, month, day):  
    print(year, month, day)
case let .string(dateStr):
    print(dateStr)
    
} // "2020-02-29"

注意上看switch內(nèi)部對(duì)let/var關(guān)鍵字的使用,如果下載枚舉值左邊,那么關(guān)聯(lián)值只能統(tǒng)一綁定給let常量或者var變量

case let .digit(year, month, day): //year、month、day都是let常量
case var .digit(year, month, day): //year、month、day都是var變量

如果let/var關(guān)鍵字寫在關(guān)聯(lián)值括號(hào)內(nèi),就比較靈活

case .digit(let year, var month, let day)

另外一些枚舉舉例

enum Password {
    case number(Int, Int, Int, Int)
    case gesture(String)
}
var pwd = Password.number(3, 5, 7, 9)
pwd = .gesture("3259")
switch pwd {
case let .number(n1 , n2 , n3 , n4 ): //數(shù)字密碼
    print("number is", n1, n2, n3, n4)
case let .gesture(pwdStr):// 字符串密碼
    print("gestrue is", pwdStr)
}


原始值(Raw Values)

枚舉成員可以只用相同類型的默認(rèn)值預(yù)先關(guān)聯(lián),這個(gè)默認(rèn)值叫做 原始值

enum PokerSuit: Character { //這里的Character表示的是枚舉值所關(guān)聯(lián)的原始值
    case spade = "?"
    case heart = "?"
    case diamond = "?"
    case club = "?"
}
var suit = PokerSuit.spade
print(suit)
print(suit.rawValue)
print(PokerSuit.club.rawValue)

enum Grade: String {
    case perfect = "A"
    case great = "B"
    case good = "C"
    case bad = "D"
}
print(Grade.perfect.rawValue) // A
print(Grade.great.rawValue) // B
print(Grade.good.rawValue) // C
print(Grade.bad.rawValue) // D


隱式原始值(Implicitly Assigned Raw Values)

enum Direction1: String {
    case north, south, east, west
}
print(Direction1.north.rawValue)

enum Direction2: String {
    case north = "nor", south, east, west
}
print(Direction2.north.rawValue)//有賦值,就用賦值的字符串
print(Direction2.south.rawValue)//沒賦值, 就用case名字符串

enum Season: Int {
    case spring, summer, autumn, winter
}
print(Season.spring.rawValue)//0
print(Season.summer.rawValue)//1
print(Season.autumn.rawValue)//2
print(Season.winter.rawValue)//3

enum Season2: Int {
    case spring = 2, summer, autumn = 6, winter
}
print(Season2.spring.rawValue) //2
print(Season2.summer.rawValue) //3
print(Season2.autumn.rawValue) //6
print(Season2.winter.rawValue) //7


遞歸枚舉(Recursive Enumeration)

//書寫方法一
indirect enum ArithExpr_1 {
    case number(Int)
    case sum(ArithExpr, ArithExpr)
    case difference(ArithExpr, ArithExpr)
}

//書寫方法二
enum ArithExpr {
    case number(Int)
    indirect case sum(ArithExpr, ArithExpr)
    indirect case difference(ArithExpr, ArithExpr)
}

let five = ArithExpr.number(5)
let four = ArithExpr.number(4)
let two = ArithExpr.number(2)
let sum = ArithExpr.sum(five, four)
let difference = ArithExpr.difference(sum, two)


func calculate(_ expr: ArithExpr) -> Int{
    switch expr {
    case let .number(value):
        return value
    case let .sum(left, right):
        return calculate(left) + calculate(right)
    case let .difference(left, right):
        return calculate(left) - calculate(right)
    }
}


MemoryLayout

我們可以使用 MemoryLayout 來獲取數(shù)據(jù)類型占用的內(nèi)存大小,相當(dāng)于C里面使用的sizeof

enum Password2 {
    case number(Int, Int, Int, Int)
    case other
}
MemoryLayout<Password2>.stride //系統(tǒng)分配給變量的內(nèi)存大小--40
MemoryLayout<Password2>.size //實(shí)際被使用的內(nèi)存大小--33
MemoryLayout<Password2>.alignment //對(duì)其參數(shù)--8

var pd = Password2.number(9, 8, 7, 6)
pd = .other
print(pd) //"other/n"
MemoryLayout.stride(ofValue: pd)  //40
MemoryLayout.size(ofValue: pd)  //33
MemoryLayout.alignment(ofValue: pd)  //8


枚舉在內(nèi)存中是如何存儲(chǔ)的?

通過MemoryLayout,我們只能簡(jiǎn)單查看一些內(nèi)存相關(guān)的信息,但還不足以看清枚舉在內(nèi)存中的具體細(xì)節(jié),由于Xcode調(diào)試工具無法為我們提供枚舉變量的內(nèi)存地址,因此需要借助一些額外的工具,這里推介一下大牛李明杰的一個(gè)工具。

(1)首先來看下一種簡(jiǎn)單的情況~~~~~~~

enum TestEnum {
    case test1, test2, test3
}
print("系統(tǒng)實(shí)際分配內(nèi)存",MemoryLayout<TestEnum>.stride)
print("實(shí)際使用的內(nèi)存",MemoryLayout<TestEnum>.size)
print("內(nèi)存對(duì)齊參數(shù)",MemoryLayout<TestEnum>.alignment)

var t = TestEnum.test1
print("枚舉變量t的內(nèi)存地址:",Mems.ptr(ofVal: &t)) //這里可以輸出變量t的內(nèi)存地址
t = .test2
t = .test3
print("Stop for debug")

Mems.ptr(ofVal: &t)可以幫我們獲得變量t的內(nèi)存地址,準(zhǔn)備好3個(gè)斷點(diǎn)

image

然后將程序運(yùn)行值斷點(diǎn)1處,此時(shí)我們已經(jīng)獲得t的內(nèi)存地址,根據(jù)該地址,調(diào)出內(nèi)存界面,我們來觀察一下此時(shí)的內(nèi)存細(xì)節(jié)
image

在繼續(xù)走到斷點(diǎn)2、斷點(diǎn)3處,對(duì)比一下各自的內(nèi)存情況如下
image

在這里插入圖片描述

小結(jié):enum TestEnum { case test1, test2, test3 }

  • 系統(tǒng)為TestEnum類型的變量分配1個(gè)字節(jié)的內(nèi)存空間
  • test1 、 test2、 test3 三個(gè)case對(duì)應(yīng)在內(nèi)存中用整數(shù)0、1、2來表示

(2)把場(chǎng)景調(diào)整為有Int型原始值的情形如下~~~~~~~

enum TestEnum: Int {
    case test1
    case test2 = 3
    case test3
    case test4 = 10
    case test5
}
print("系統(tǒng)實(shí)際分配內(nèi)存",MemoryLayout<TestEnum>.stride)
print("實(shí)際使用的內(nèi)存",MemoryLayout<TestEnum>.size)
print("內(nèi)存對(duì)齊參數(shù)",MemoryLayout<TestEnum>.alignment)

var t = TestEnum.test1
print("枚舉變量t的內(nèi)存地址:",Mems.ptr(ofVal: &t)) //這里可以輸出變量t的內(nèi)存地址
t = .test2
t = .test3
t = .test4
t = .test5
print("Stop for debug")

按照上面同樣的方法,對(duì)比各自case的內(nèi)存情況如下

image

image

image

image

image

我們?cè)诓榭匆幌赂髯?code>case的rawValue

print("test1的rawValue:", TestEnum.test1.rawValue)
print("test2的rawValue:", TestEnum.test2.rawValue)
print("test2的rawValue:", TestEnum.test3.rawValue)
print("test2的rawValue:", TestEnum.test4.rawValue)
print("test2的rawValue:", TestEnum.test5.rawValue)

***********運(yùn)行結(jié)果
test1的rawValue: 0
test2的rawValue: 3
test2的rawValue: 4
test2的rawValue: 10
test2的rawValue: 11

看得出,如果原始值類型為Int

  • 那么在不手動(dòng)設(shè)定的情況下,首個(gè)case的原始值默為整數(shù)0,非首個(gè)case的默認(rèn)值為上一個(gè)case的默認(rèn)值+1
  • 如果手動(dòng)設(shè)定了,那么原始值即為設(shè)定值。

(3)看過了帶Int型原始值的情況之后,在看一下帶String型原始值的情況,改造如下~~~~~~~

enum TestEnum: String {
    case test1
    case test2 = "AA"
    case test3 = "漢字"
    case test4 = "??"
    case test5
}
print("系統(tǒng)實(shí)際分配內(nèi)存",MemoryLayout<TestEnum>.stride)
print("實(shí)際使用的內(nèi)存",MemoryLayout<TestEnum>.size)
print("內(nèi)存對(duì)齊參數(shù)",MemoryLayout<TestEnum>.alignment)

var t = TestEnum.test1
print("枚舉變量t的內(nèi)存地址:",Mems.ptr(ofVal: &t)) //這里可以輸出變量t的內(nèi)存地址
t = .test2
t = .test3
t = .test4
t = .test5
print("Stop for debug")


print("test1的rawValue:", TestEnum.test1.rawValue)
print("test2的rawValue:", TestEnum.test2.rawValue)
print("test2的rawValue:", TestEnum.test3.rawValue)
print("test2的rawValue:", TestEnum.test4.rawValue)
print("test2的rawValue:", TestEnum.test5.rawValue)

****************運(yùn)行結(jié)果
系統(tǒng)實(shí)際分配內(nèi)存 1
實(shí)際使用的內(nèi)存 1
內(nèi)存對(duì)齊參數(shù) 1
枚舉變量t的內(nèi)存地址: 0x0000000100008218
Stop for debug
test1的rawValue: test1
test2的rawValue: AA
test2的rawValue: 漢字
test2的rawValue: ??
test2的rawValue: test5
Program ended with exit code: 0

內(nèi)存的情況這里省略,和上面Int型的時(shí)候是一樣的,根據(jù)調(diào)試輸出的情況,我們可以看出

  • 如果不設(shè)置原始值,那么case的原始值為該case名稱的字符串
  • 如果設(shè)置了原始值,那嗎case的原始值即為設(shè)定值

總結(jié) 帶原始值的枚舉

  • 枚舉變量本身的就占一個(gè)字節(jié)
  • 枚舉變量所對(duì)應(yīng)的內(nèi)存里所存放的具體值:對(duì)應(yīng)第一個(gè)case為0,并且往后逐個(gè)+1

(4)帶關(guān)聯(lián)值的場(chǎng)景~~~~~~~

enum TestEnum {
    case test1(a: Int, b: Int, c: Int)
    case test2(d: Int, e: Int)
    case test3(f: Int)
    case test4(g: Bool)
    case test5
}
print("系統(tǒng)實(shí)際分配內(nèi)存",MemoryLayout<TestEnum>.stride)
print("實(shí)際使用的內(nèi)存",MemoryLayout<TestEnum>.size)
print("內(nèi)存對(duì)齊參數(shù)",MemoryLayout<TestEnum>.alignment)

var t = TestEnum.test1(a: 1, b: 2, c: 3)
//這里可以輸出變量t的內(nèi)存地址
print("枚舉變量t的內(nèi)存地址:",Mems.ptr(ofVal: &t))
t = .test2(d: 4, e: 5)
t = .test3(f: 6)
t = .test4(g: true)
t = .test5
print("Stop for debug")

*****************運(yùn)行結(jié)果
系統(tǒng)實(shí)際分配內(nèi)存 32
實(shí)際使用的內(nèi)存 25
內(nèi)存對(duì)齊參數(shù) 8
枚舉變量t的內(nèi)存地址: 0x0000000100008208

接下來照例在過一遍內(nèi)存,下面直接貼上內(nèi)存查看的結(jié)果

  • t = test1(a: 1, b: 2, c: 3)
01 00 00 00 00 00 00 00  --> 對(duì)應(yīng)a
02 00 00 00 00 00 00 00  --> 對(duì)應(yīng)b
03 00 00 00 00 00 00 00  --> 對(duì)應(yīng)c
00 00 00 00 00 00 00 00  --> 對(duì)應(yīng)case test1
  • t = test2(d: 4, e: 5)
04 00 00 00 00 00 00 00  --> 對(duì)應(yīng)d
05 00 00 00 00 00 00 00  --> 對(duì)應(yīng)e
00 00 00 00 00 00 00 00  --> 此時(shí)沒用到
01 00 00 00 00 00 00 00  --> 對(duì)應(yīng)case test2
  • t = test3(f: 6)
06 00 00 00 00 00 00 00  --> 對(duì)應(yīng)f
00 00 00 00 00 00 00 00  --> 此時(shí)沒用到
00 00 00 00 00 00 00 00  --> 此時(shí)沒用到
02 00 00 00 00 00 00 00  --> 對(duì)應(yīng)case test3
  • t = test4(g: true)
01 00 00 00 00 00 00 00  --> 對(duì)應(yīng)g
00 00 00 00 00 00 00 00  --> 此時(shí)沒用到
00 00 00 00 00 00 00 00  --> 此時(shí)沒用到
03 00 00 00 00 00 00 00  --> 對(duì)應(yīng)case test4
  • t = test5
00 00 00 00 00 00 00 00  --> 此時(shí)沒用到
00 00 00 00 00 00 00 00  --> 此時(shí)沒用到
00 00 00 00 00 00 00 00  --> 此時(shí)沒用到
04 00 00 00 00 00 00 00  --> 對(duì)應(yīng)case test5

總結(jié) 帶關(guān)聯(lián)值的枚舉

  • 枚舉變量的成員case的值只用了其內(nèi)存空間的1字節(jié)來存放
  • 枚舉的case關(guān)聯(lián)值也存放在枚舉變量的內(nèi)存中
  • 系統(tǒng)為枚舉的case關(guān)聯(lián)值所分配的內(nèi)存空間,必須保證可以放下所需內(nèi)存最大的那個(gè)關(guān)聯(lián)值
  • 枚舉變量的內(nèi)存空間里,先存放存放的是case關(guān)聯(lián)值,成員case的值被放在最后
  • 枚舉變量的內(nèi)存總空間按內(nèi)存對(duì)齊參數(shù)進(jìn)行補(bǔ)齊(計(jì)算機(jī)常識(shí))

(5)一些極端場(chǎng)景~~~~~~~

enum TestEnum {
    case test
}

print("系統(tǒng)實(shí)際分配內(nèi)存",MemoryLayout<TestEnum>.stride)
print("實(shí)際使用的內(nèi)存",MemoryLayout<TestEnum>.size)
print("內(nèi)存對(duì)齊參數(shù)",MemoryLayout<TestEnum>.alignment)
var t = TestEnum.test
print(print("枚舉變量t的內(nèi)存地址:",Mems.ptr(ofVal: &t)))

****************運(yùn)行結(jié)果
系統(tǒng)實(shí)際分配內(nèi)存 1
實(shí)際使用的內(nèi)存 0
內(nèi)存對(duì)齊參數(shù) 1
枚舉變量t的內(nèi)存地址: 0x0000000000000001
Program ended with exit code: 0

可以看到,系統(tǒng)確實(shí)是分配了1個(gè)字節(jié)給枚舉,但是實(shí)際上用到了0個(gè),因?yàn)橐环N情況不需要做任何區(qū)分,所以也就不需要存儲(chǔ),當(dāng)然貌似沒人會(huì)這么用,所以系統(tǒng)針對(duì)這種情況下的處理,就不難理解了。在看看帶關(guān)聯(lián)值的情況:

enum TestEnum {
    case test(Int)
}

print("系統(tǒng)實(shí)際分配內(nèi)存",MemoryLayout<TestEnum>.stride)
print("實(shí)際使用的內(nèi)存",MemoryLayout<TestEnum>.size)
print("內(nèi)存對(duì)齊參數(shù)",MemoryLayout<TestEnum>.alignment)
var t = TestEnum.test(10)
print("枚舉變量t的內(nèi)存地址:",Mems.ptr(ofVal: &t))
print("Stop for debug")

***************運(yùn)行結(jié)果
系統(tǒng)實(shí)際分配內(nèi)存 8
實(shí)際使用的內(nèi)存 8
內(nèi)存對(duì)齊參數(shù) 8
枚舉變量t的內(nèi)存地址: 0x0000000100007200
Stop for debug
Program ended with exit code: 0

***************匯編結(jié)果
0A 00 00 00 00 00 00 00 

可以看到系統(tǒng)直接分配了8個(gè)字節(jié)來存儲(chǔ)枚舉里面的Int型關(guān)聯(lián)值,沒有分配空間來存儲(chǔ)成員case的值,原因和上面很想,因?yàn)楝F(xiàn)在就是一種case,沒有必要再存儲(chǔ)成員變量的值,只需要關(guān)心case關(guān)聯(lián)值就好。那如果有一個(gè)以上的case,是不是就會(huì)給成員case分配空間了?咱們?cè)囋嚳?,如?/p>

enum TestEnum {
    case other
    case test(Int)
}

print("系統(tǒng)實(shí)際分配內(nèi)存",MemoryLayout<TestEnum>.stride)
print("實(shí)際使用的內(nèi)存",MemoryLayout<TestEnum>.size)
print("內(nèi)存對(duì)齊參數(shù)",MemoryLayout<TestEnum>.alignment)

var t = TestEnum.other
//Mem.memStr是大神李明杰提供的工具,文中有鏈接,可以幫我直接獲取變量的內(nèi)存里面的值
print("枚舉變量t = other 時(shí)的內(nèi)存情況:  ",Mems.memStr(ofVal: &t)) 
t = TestEnum.test(10)
print("枚舉變量t = test(10)時(shí)的內(nèi)存地址:",Mems.memStr(ofVal: &t))
print("Stop for debug")

***************運(yùn)行結(jié)果
系統(tǒng)實(shí)際分配內(nèi)存 16
實(shí)際使用的內(nèi)存 9
內(nèi)存對(duì)齊參數(shù) 8
枚舉變量t = other 時(shí)的內(nèi)存情況:   0x0000000000000000 0x0000000000000001
枚舉變量t = test(10)時(shí)的內(nèi)存地址: 0x000000000000000a 0x0000000000000000

可以看出,只要case大于1個(gè),除了Int型關(guān)聯(lián)值需要占用8個(gè)字節(jié)外,枚舉變量還使用了1個(gè)字節(jié)來存儲(chǔ)成員case的值,根據(jù)內(nèi)存對(duì)齊參數(shù)8,系統(tǒng)給枚舉變量分配了16字節(jié)空間。上面的結(jié)果中,最后一個(gè)字節(jié)是用來存放成員case的值也就是case other 對(duì)應(yīng)了01, case test 對(duì)應(yīng)了00,但是感覺順序不太對(duì),明明是other在前,test在后的,帶著這個(gè)疑問,我們把用例改造如下

enum TestEnum {
    case aaa
    case test(Int)
    case ccc
    case test3(Int, Int)
    case test2(Int,Int, Int)
    case other
}
print("系統(tǒng)實(shí)際分配內(nèi)存",MemoryLayout<TestEnum>.stride)
print("實(shí)際使用的內(nèi)存",MemoryLayout<TestEnum>.size)
print("內(nèi)存對(duì)齊參數(shù)",MemoryLayout<TestEnum>.alignment)

var t = TestEnum.aaa
print("t = .aaa的內(nèi)存情況:              ",Mems.memStr(ofVal: &t))
t = TestEnum.test(10)
print("t = .test(10)的內(nèi)存情況:         ",Mems.memStr(ofVal: &t))
t = TestEnum.ccc
print("t = .ccc的內(nèi)存情況:              ",Mems.memStr(ofVal: &t))
t = TestEnum.test3(16, 32)
print("t = .test3(16, 32)的內(nèi)存情況:    ",Mems.memStr(ofVal: &t))
t = TestEnum.test2(20, 20, 20)
print("t = .test2(20, 20, 20)的內(nèi)存情況:",Mems.memStr(ofVal: &t))
t = TestEnum.other
print("t = .other的內(nèi)存情況:            ",Mems.memStr(ofVal: &t))
print("Stop for debug")



*************************************運(yùn)行結(jié)果
系統(tǒng)實(shí)際分配內(nèi)存 32
實(shí)際使用的內(nèi)存 25
內(nèi)存對(duì)齊參數(shù) 8
t = .aaa的內(nèi)存情況:               0x0000000000000000 0x0000000000000000 0x0000000000000000 0x0000000000000003
t = .test(10)的內(nèi)存情況:          0x000000000000000a 0x0000000000000000 0x0000000000000000 0x0000000000000000
t = .ccc的內(nèi)存情況:               0x0000000000000001 0x0000000000000000 0x0000000000000000 0x0000000000000003
t = .test3(16, 32)的內(nèi)存情況:     0x0000000000000010 0x0000000000000020 0x0000000000000000 0x0000000000000001
t = .test2(20, 20, 20)的內(nèi)存情況: 0x0000000000000014 0x0000000000000014 0x0000000000000014 0x0000000000000002
t = .other的內(nèi)存情況:             0x0000000000000002 0x0000000000000000 0x0000000000000000 0x0000000000000003
Stop for debug
Program ended with exit code: 0

從上面的調(diào)試,又挖掘了一點(diǎn)小細(xì)節(jié):


image
  • 對(duì)于有關(guān)聯(lián)值的成員case,它的case值會(huì)根據(jù)定義的順序,默認(rèn)從0開始+1累加,
  • 其余所有不帶關(guān)聯(lián)值的成員case,它們的case值相同,而且都等于最后一個(gè)可關(guān)聯(lián)成員case 的值+1


關(guān)聯(lián)值 VS 原始值rawValue

以上我們看清楚了簡(jiǎn)單枚舉、關(guān)聯(lián)值枚舉、原始值枚舉在內(nèi)存中分別是如何存儲(chǔ)的,可以看出,枚舉的關(guān)聯(lián)值和原始值又以下區(qū)別:

  • 內(nèi)存角度:關(guān)聯(lián)值是直接存儲(chǔ)在枚舉變量?jī)?nèi)存里面的,而原始值則不是,因?yàn)樵贾凳峭ㄟ^xx.rawValue訪問的,因此它的值完全不需要存儲(chǔ),可以在枚舉定義完之后通過方法提供給外部。
  • 使用角度:原始值必須在枚舉定義的時(shí)候確定原始值類型,才能被使用 enum Direction : String/Int/... {...}。關(guān)聯(lián)值則必須在枚舉定義的時(shí)候,確定好case所對(duì)應(yīng)的關(guān)聯(lián)值類型
  • 賦值:關(guān)聯(lián)值只能在枚舉case被賦值給變量的時(shí)候進(jìn)行賦值,因?yàn)橥粋€(gè)case每次被賦值給變量,都需要設(shè)定一個(gè)關(guān)聯(lián)值,因此也可以說關(guān)聯(lián)值是可以改變的,如下
enum Score {
    case points(Int)
    case grade(Character)
}
var score = Score.points(96)
score = .grade("A")
score = .grade("B") -->相同的case,不同的關(guān)聯(lián)值

而原始值,只能在枚舉定義的時(shí)候進(jìn)行賦值,不賦值則系統(tǒng)會(huì)給定相應(yīng)的默認(rèn)值,也就是只有一次機(jī)會(huì)可以賦值,定義完枚舉之后,就沒有辦法可以更改原始值了,示例如下

enum Grade: String {
    case perfect = "A"
    case great
    case good = "C"
    case bad = "D"
}
print(Grade.perfect.rawValue) --> A
print(Grade.great.rawValue) --> 定義時(shí)無賦值,系統(tǒng)默認(rèn)為case的名稱 great
print(Grade.good.rawValue) --> C
print(Grade.bad.rawValue) -> D


switch的實(shí)現(xiàn)原理(待續(xù)...)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容