swift--枚舉

C語言枚舉

enum 枚舉名 { 
 枚舉值1, 
 枚舉值2, 
 ……};

一周七天可以寫成

enum week 
 { 
   MON, TUE, WED, THU, FRI, SAT, SUN 
 };

第?個(gè)枚舉成員的默認(rèn)值為整型的 0,后?的枚舉值依次類推憋活,如果我們想更改,只需要這樣操作

enum week 
 { 
   MON = 1, TUE, WED, THU, FRI, SAT, SUN 
 };

那如何定義?個(gè)枚舉變量


enum week 
 { 
   MON = 1, TUE, WED, THU, FRI, SAT, SUN 
 } week;
//可以省略聲明的枚舉
enum week 
 { 
   MON = 1, TUE, WED, THU, FRI, SAT, SUN 
 } week;

Swift 枚舉寫法

enum week{
    case MONDAY
    case TUEDAY
    case WEDDAY
    case THUDAY
    case FRIDAY
    case SATDAY
    case SUNDAY
}

上述代碼也可以直接?個(gè) case ,然后?逗號隔開

enum week{
    case MONDAY, TUEDAY, WEDDAY, THUDAY, FRIDAY, SATDAY, SUNDAY
}

枚舉值默認(rèn)是整形,也以表達(dá)為String

enum week: String
{
    case MON = "MON"
    case TUE = "TUE"
    case WED = "WED"
    case THU = "THU"
    case FRI = "FRI"
    case SAT = "SAT"
    case SUN = "SUN"
}

我們賦值的字符串叫做原始值(RawValue),,如果我們不想寫后?的字符串糠惫,這個(gè)時(shí)候我們就 可以使? 隱? RawValue 分配

enum week: Int {  case mon, tue, wed, thu, fri = 10, sat, sun  }
print(week.fri.rawValue)
···········
10


enum week: String {  case MON, TUE, WED, THU, FRI , SAT, SUN  }
print(week.FRI.rawValue)
···········
FRI

當(dāng)改成String型之后拉盾,打印的值就變成了當(dāng)前枚舉值了。
通過swiftc -emit-sil main.swift | xcrun swift-demangle > ./main.sil && open main.sil查看sil代碼

enum week : String {
  case MON, TUE, WED, THU, FRI, SAT, SUN
  typealias RawValue = String
  init?(rawValue: String)
  var rawValue: String { get }
}


// main
sil @main : $@convention(c) (Int32, UnsafeMutablePointer<Optional<UnsafeMutablePointer<Int8>>>) -> Int32 {
bb0(%0 : $Int32, %1 : $UnsafeMutablePointer<Optional<UnsafeMutablePointer<Int8>>>):
  alloc_global @main.w : Swift.String                     // id: %2
  %3 = global_addr @main.w : Swift.String : $*String      // user: %8
  %4 = metatype $@thin week.Type
  %5 = enum $week, #week.MON!enumelt              // user: %7
  // function_ref week.rawValue.getter
  %6 = function_ref @main.week.rawValue.getter : Swift.String : $@convention(method) (week) -> @owned String // user: %7
  %7 = apply %6(%5) : $@convention(method) (week) -> @owned String // user: %8
  store %7 to %3 : $*String                       // id: %8
  %9 = integer_literal $Builtin.Int32, 0          // user: %10
  %10 = struct $Int32 (%9 : $Builtin.Int32)       // user: %11
  return %10 : $Int32                             // id: %11
} // end sil function 'main'

%6調(diào)用了get方法仍侥,7%將%5也就是枚舉值MON傳參進(jìn)去


// week.rawValue.getter
sil hidden @main.week.rawValue.getter : Swift.String : $@convention(method) (week) -> @owned String {
// %0                                             // users: %2, %1
bb0(%0 : $week):
//聲明一個(gè)變量self要出,等于參數(shù)week
  debug_value %0 : $week, let, name "self", argno 1 // id: %1
//匹配枚舉值,跳轉(zhuǎn)到對應(yīng)的分支
  switch_enum %0 : $week, case #week.MON!enumelt: bb1, case #week.TUE!enumelt: bb2, case #week.WED!enumelt: bb3, case #week.THU!enumelt: bb4, case #week.FRI!enumelt: bb5, case #week.SAT!enumelt: bb6, case #week.SUN!enumelt: bb7 // id: %2
//創(chuàng)建String农渊,跳轉(zhuǎn)bb8
bb1:                                              // Preds: bb0
  %3 = string_literal utf8 "MON"                  // user: %8
  %4 = integer_literal $Builtin.Word, 3           // user: %8
  %5 = integer_literal $Builtin.Int1, -1          // user: %8
  %6 = metatype $@thin String.Type                // user: %8
  // function_ref String.init(_builtinStringLiteral:utf8CodeUnitCount:isASCII:)
  %7 = function_ref @Swift.String.init(_builtinStringLiteral: Builtin.RawPointer, utf8CodeUnitCount: Builtin.Word, isASCII: Builtin.Int1) -> Swift.String : $@convention(method) (Builtin.RawPointer, Builtin.Word, Builtin.Int1, @thin String.Type) -> @owned String // user: %8
  %8 = apply %7(%3, %4, %5, %6) : $@convention(method) (Builtin.RawPointer, Builtin.Word, Builtin.Int1, @thin String.Type) -> @owned String // user: %9
  br bb8(%8 : $String)                            // id: %9
...
//返回
// %52                                            // user: %53
bb8(%52 : $String):                               // Preds: bb7 bb6 bb5 bb4 bb3 bb2 bb1
  return %52 : $String                            // id: %53
} // end sil function 'main.week.rawValue.getter : Swift.String'

case 和 rawValue

print(week.MON)
print(week.MON.rawValue)

雖然打印的都是MON患蹂,但是一個(gè)是枚舉類型,一個(gè)是字符串類型

枚舉的init

通過sil文件查看init方法


// week.init(rawValue:)
sil hidden @main.week.init(rawValue: Swift.String) -> main.week? : $@convention(method) (@owned String, @thin week.Type) -> Optional<week> {
// %0                                             // users: %164, %158, %79, %3
bb0(%0 : $String, %1 : $@thin week.Type):
  %2 = alloc_stack $week, var, name "self"        // users: %162, %154, %143, %132, %121, %110, %99, %88, %165, %159
  debug_value %0 : $String, let, name "rawValue", argno 1 // id: %3
  %4 = integer_literal $Builtin.Word, 7           // user: %6
  // function_ref _allocateUninitializedArray<A>(_:)
  %5 = function_ref @Swift._allocateUninitializedArray<A>(Builtin.Word) -> ([A], Builtin.RawPointer) : $@convention(thin) <τ_0_0> (Builtin.Word) -> (@owned Array<τ_0_0>, Builtin.RawPointer) // user: %6
  %6 = apply %5<StaticString>(%4) : $@convention(thin) <τ_0_0> (Builtin.Word) -> (@owned Array<τ_0_0>, Builtin.RawPointer) // users: %8, %7
//構(gòu)建元組砸紊,存放字符串?dāng)?shù)組和指針地址
  %7 = tuple_extract %6 : $(Array<StaticString>, Builtin.RawPointer), 0 // users: %80, %79
  %8 = tuple_extract %6 : $(Array<StaticString>, Builtin.RawPointer), 1 // user: %9
  %9 = pointer_to_address %8 : $Builtin.RawPointer to [strict] $*StaticString // users: %17, %69, %59, %49, %39, %29, %19
  %10 = string_literal utf8 "MON"                 // user: %12
  %11 = integer_literal $Builtin.Word, 3          // user: %16
  %12 = builtin "ptrtoint_Word"(%10 : $Builtin.RawPointer) : $Builtin.Word // user: %16
  br bb1                                          // id: %13

bb1:                                              // Preds: bb0
  %14 = integer_literal $Builtin.Int8, 2          // user: %16
  br bb2                                          // id: %15

bb2:                                              // Preds: bb1
  %16 = struct $StaticString (%12 : $Builtin.Word, %11 : $Builtin.Word, %14 : $Builtin.Int8) // user: %17
  store %16 to %9 : $*StaticString                // id: %17
  %18 = integer_literal $Builtin.Word, 1          // user: %19
  %19 = index_addr %9 : $*StaticString, %18 : $Builtin.Word // user: %27
  %20 = string_literal utf8 "TUE"                 // user: %22
  %21 = integer_literal $Builtin.Word, 3          // user: %26
  %22 = builtin "ptrtoint_Word"(%20 : $Builtin.RawPointer) : $Builtin.Word // user: %26
  br bb3                                          // id: %23


//通過方法來進(jìn)行匹配
bb14:                                             // Preds: bb13
  %76 = struct $StaticString (%72 : $Builtin.Word, %71 : $Builtin.Word, %74 : $Builtin.Int8) // user: %77
  store %76 to %69 : $*StaticString               // id: %77
  // function_ref _findStringSwitchCase(cases:string:)
  %78 = function_ref @Swift._findStringSwitchCase(cases: [Swift.StaticString], string: Swift.String) -> Swift.Int : $@convention(thin) (@guaranteed Array<StaticString>, @guaranteed String) -> Int // user: %79
  %79 = apply %78(%7, %0) : $@convention(thin) (@guaranteed Array<StaticString>, @guaranteed String) -> Int // users: %149, %138, %127, %116, %105, %94, %83, %147, %136, %125, %114, %103, %92, %81
  release_value %7 : $Array<StaticString>         // id: %80
  debug_value %79 : $Int, let, name "$match"      // id: %81
  %82 = integer_literal $Builtin.Int64, 0         // user: %84
  %83 = struct_extract %79 : $Int, #Int._value    // user: %84
  %84 = builtin "cmp_eq_Int64"(%82 : $Builtin.Int64, %83 : $Builtin.Int64) : $Builtin.Int1 // user: %85
  cond_br %84, bb15, bb16  


//如果成功传于,返回
bb15:                                             // Preds: bb14
  %86 = metatype $@thin week.Type
  %87 = enum $week, #week.MON!enumelt             // user: %89
  %88 = begin_access [modify] [static] %2 : $*week // users: %89, %90
  store %87 to %88 : $*week                       // id: %89
  end_access %88 : $*week                         // id: %90
  br bb29                                         // id: %91
//不成功繼續(xù)匹配
bb16:                                             // Preds: bb14
  debug_value %79 : $Int, let, name "$match"      // id: %92
  %93 = integer_literal $Builtin.Int64, 1         // user: %95
  %94 = struct_extract %79 : $Int, #Int._value    // user: %95
  %95 = builtin "cmp_eq_Int64"(%93 : $Builtin.Int64, %94 : $Builtin.Int64) : $Builtin.Int1 // user: %96
  cond_br %95, bb17, bb18                         // id: %96



bb29:                                             // Preds: bb27 bb25 bb23 bb21 bb19 bb17 bb15
  %162 = load %2 : $*week                         // user: %163
//返回的是可選類型,如果沒有就是nil
  %163 = enum $Optional<week>, #Optional.some!enumelt.1, %162 : $week // user: %166
  release_value %0 : $String                      // id: %164
  dealloc_stack %2 : $*week                       // id: %165
  br bb30(%163 : $Optional<week>)  


// %167                                           // user: %168
bb30(%167 : $Optional<week>):                     // Preds: bb29 bb28
  return %167 : $Optional<week>                   // id: %168
} // end sil function 'main.week.init(rawValue: Swift.String) -> main.week?'

枚舉的遍歷

enum week: String {  case MON, TUE, WED, THU, FRI , SAT, SUN  }

extension week: CaseIterable{}
var allCase = week.allCases
for c in allCase {
    print(c)
}

只要實(shí)現(xiàn)了CaseIterable協(xié)議就可以醉顽。

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

如果我們想?枚舉表達(dá)更復(fù)雜的信息沼溜,?不僅僅是?個(gè) RawValue 這么簡單,這個(gè)時(shí)候我們就可以使? Associated Value,當(dāng)聲明了關(guān)聯(lián)值后游添,就無法使用initRawValue

enum Shape{
    case circle(radious: Double)
    case rectangle(width: Int, height: Int)
}

var circle = Shape.circle(radious: 10.0)
circle = Shape.circle(radious: 20.0)
switch circle{
case let .circle(radious):
    print(radious)
case .rectangle(let width, var height):
    height += 10
    print(width,height)
}
//也可以單獨(dú)取出來用
if case let Shape.circle(radious) = circle{
    print(radious)
}

當(dāng)我們只關(guān)注不同枚舉值的相同關(guān)聯(lián)值

enum Shape{
    case rectangle(width: Int, height: Int)
    case squar(width1: Int, height1: Int)
}
var circle = Shape.rectangle(width: 30, height: 40)
switch circle{
case let .rectangle(10, x), let .squar(width1: 10, height1: x):
    print(x)
default:
    print("nil")
}

注意系草,前后變量要一一對應(yīng)。不關(guān)注的也以用_來省略

枚舉的嵌套

枚舉中含有枚舉
 enum CombineDirect{
    enum BaseDirect{
        case up
        case down
        case left
        case right
    }
    case leftUp(combineElement1: BaseDirect, combineElement2: BaseDirect)
    case rightUp(combineElement1: BaseDirect, combineElement2: BaseDirect)
    case leftDown(combineElement1: BaseDirect, combineElement2: BaseDirect)
    case rightDown(combineElement1: BaseDirect, combineElement2: BaseDirect)
}
let combind = CombineDirect.leftDown(combineElement1: .left, combineElement2: .down)
結(jié)構(gòu)體中包含枚舉
struct Skill{
    enum KeyType{
        case up
        case down
        case left
        case right
    }
    let key: KeyType
    func launchSkill(){
        switch key {
        case .left,.right:
            print("left, right")
        case .down,.up:
            print("up, down")
        }
    }
}

枚舉中包含屬性

enum 中能包含計(jì)算屬性唆涝,類型屬性悄但。不能包含存儲屬性

 enum Shape{
    case circle(radious: Double)
    case rectangle(width: Int, height: Int)
//    var radious: Double
    static var height = 20.0
    var width: Double{
        get{
            return 10.0
        }
    }
}

枚舉中包含方法

enum week: Int {
    case MON, TUE, WED, THU, FRI , SAT, SUN
    
    mutating func nextDay(){
        if self == .SUN{
            self = week(rawValue: 0)!
        }else{
            self = week(rawValue: self.rawValue + 1)!
        }
    }
}

枚舉的大小

枚舉的大小取決于枚舉值,默認(rèn)是UInt8大小石抡,也就是一字節(jié)檐嚣,超過UInt8容量就會升級為UInt16,也就是二字節(jié)啰扛,以此類推嚎京。
存在關(guān)聯(lián)值,取決最大case的大小


 enum Shape{
    case circle(radious: Double)
    case rectangle(width: Int)
}

print(MemoryLayout<Shape>.stride)
print(MemoryLayout<Shape>.size)
·····
16
9

Double占8字節(jié)隐解,加上case的1字節(jié)鞍帝,就是9字節(jié),根據(jù)字節(jié)對齊煞茫,所以步長是16帕涌。

indirect

enum List<T>{
    case end
    indirect case node(T, next: List<T>)
}

print(MemoryLayout<List<String>>.size)
print(MemoryLayout<List<String>>.stride)
·············
8
8

打印添加了indirect的枚舉大小為8字節(jié)

var node = List<Int>.node(10, next: List<Int>.end)
·················
(lldb) p withUnsafePointer(to: &node, {$0})
(UnsafePointer<swiftTest.List<Int>>) $R6 = 0x0000000100003068
(lldb) x/4g 0x0000000100003068
0x100003068: 0x0000000100419fd0 0x00007fff98c81218
0x100003078: 0x00007fff98c81218 0x00000001007824d0
(lldb) x/4g 0x0000000100419fd0
0x100419fd0: 0x0000000100002030 0x0000000000000002
0x100419fe0: 0x000000000000000a 0x0000000000000000

通過打印地址摄凡,發(fā)現(xiàn)把聲明了indirect關(guān)鍵字的值放在了堆空間上。
查看sil

// main
sil @main : $@convention(c) (Int32, UnsafeMutablePointer<Optional<UnsafeMutablePointer<Int8>>>) -> Int32 {
bb0(%0 : $Int32, %1 : $UnsafeMutablePointer<Optional<UnsafeMutablePointer<Int8>>>):
  alloc_global @main.node : main.List<Swift.Int>        // id: %2
  %3 = global_addr @main.node : main.List<Swift.Int> : $*List<Int> // user: %16
  %4 = metatype $@thin List<Int>.Type
  %5 = integer_literal $Builtin.Int64, 10         // user: %6
  %6 = struct $Int (%5 : $Builtin.Int64)          // user: %13
  %7 = metatype $@thin List<Int>.Type
  %8 = enum $List<Int>, #List.end!enumelt         // user: %14
  %9 = alloc_box $<τ_0_0> { var (τ_0_0, next: List<τ_0_0>) } <Int> // users: %15, %10
  %10 = project_box %9 : $<τ_0_0> { var (τ_0_0, next: List<τ_0_0>) } <Int>, 0 // users: %12, %11
  %11 = tuple_element_addr %10 : $*(Int, next: List<Int>), 0 // user: %13
  %12 = tuple_element_addr %10 : $*(Int, next: List<Int>), 1 // user: %14
  store %6 to %11 : $*Int                         // id: %13
  store %8 to %12 : $*List<Int>                   // id: %14
  %15 = enum $List<Int>, #List.node!enumelt.1, %9 : $<τ_0_0> { var (τ_0_0, next: List<τ_0_0>) } <Int> // user: %16
  store %15 to %3 : $*List<Int>                   // id: %16
  %17 = integer_literal $Builtin.Int32, 0         // user: %18
  %18 = struct $Int32 (%17 : $Builtin.Int32)      // user: %19
  return %18 : $Int32                             // id: %19
} // end sil function 'main'

通過官方文檔可以查看alloc_box的解釋

Allocates a reference-counted @box on the heap large enough to hold a value of type T, along with a retain count and any other metadata required by the runtime. The result of the instruction is the reference-counted @box reference that owns the box. The project_box instruction is used to retrieve the address of the value inside the box.
在堆上分配一個(gè)引用計(jì)數(shù)的@box蚓曼,其大小足以容納類型T的值亲澡,以及retain計(jì)數(shù)和運(yùn)行時(shí)所需的任何其他元數(shù)據(jù)。該指令的結(jié)果是引用計(jì)數(shù)的@box引用纫版,該引用擁有該框床绪。project_box指令用于檢索框內(nèi)值的地址。

OC-Swift混編

我們在Swift的枚舉前加上@objc就可以了

@objc enum WEEK: Int {
    case MON, TUE
}

編譯一下其弊,我們就可以在.h文件中查看


查看轉(zhuǎn)譯文件.png
typedef SWIFT_ENUM(NSInteger, WEEK, closed) {
  WEEKMON = 0,
  WEEKTUE = 1,
};

OC僅能使用聲明為Int類型的癞己。

NS_ENUM
**********OC**********
NS_ENUM(NSInteger, OCENUM){
    Value1,
    Value2
};


**********SWIFT**********
public var OCENUM: OCENUM

public enum OCENUM : Int {

    
    case Value1 = 0

    case Value2 = 1
}
typedef enum

如果用typedef enum來聲明,就會編譯成結(jié)構(gòu)體

**********OC**********
typedef enum {
    Num1,
    Num2
} OCNum;
**********SWIFT**********
public struct OCNum : Equatable, RawRepresentable {

    public init(_ rawValue: UInt32)

    public init(rawValue: UInt32)

    public var rawValue: UInt32
}
typedef NS_ENUM
**********OC**********
typedef NS_ENUM(NSInteger, CEnum) {
    CEnumInvalid = 0,
    CEnumA = 1,
    CEnumB,
    CEnumC
};
**********SWIFT**********
public enum CEnum : Int {

    
    case invalid = 0

    case A = 1

    case B = 2

    case C = 3
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末梭伐,一起剝皮案震驚了整個(gè)濱河市痹雅,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌糊识,老刑警劉巖练慕,帶你破解...
    沈念sama閱讀 211,639評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異技掏,居然都是意外死亡铃将,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,277評論 3 385
  • 文/潘曉璐 我一進(jìn)店門哑梳,熙熙樓的掌柜王于貴愁眉苦臉地迎上來劲阎,“玉大人,你說我怎么就攤上這事鸠真∶跸桑” “怎么了?”我有些...
    開封第一講書人閱讀 157,221評論 0 348
  • 文/不壞的土叔 我叫張陵吠卷,是天一觀的道長锡垄。 經(jīng)常有香客問我,道長祭隔,這世上最難降的妖魔是什么货岭? 我笑而不...
    開封第一講書人閱讀 56,474評論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮疾渴,結(jié)果婚禮上千贯,老公的妹妹穿的比我還像新娘。我一直安慰自己搞坝,他們只是感情好搔谴,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,570評論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著桩撮,像睡著了一般敦第。 火紅的嫁衣襯著肌膚如雪峰弹。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,816評論 1 290
  • 那天芜果,我揣著相機(jī)與錄音鞠呈,去河邊找鬼。 笑死师幕,一個(gè)胖子當(dāng)著我的面吹牛粟按,可吹牛的內(nèi)容都是我干的诬滩。 我是一名探鬼主播霹粥,決...
    沈念sama閱讀 38,957評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼疼鸟!你這毒婦竟也來了后控?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,718評論 0 266
  • 序言:老撾萬榮一對情侶失蹤空镜,失蹤者是張志新(化名)和其女友劉穎浩淘,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體吴攒,經(jīng)...
    沈念sama閱讀 44,176評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡张抄,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,511評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了洼怔。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片署惯。...
    茶點(diǎn)故事閱讀 38,646評論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖镣隶,靈堂內(nèi)的尸體忽然破棺而出极谊,到底是詐尸還是另有隱情,我是刑警寧澤安岂,帶...
    沈念sama閱讀 34,322評論 4 330
  • 正文 年R本政府宣布轻猖,位于F島的核電站,受9級特大地震影響域那,放射性物質(zhì)發(fā)生泄漏咙边。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,934評論 3 313
  • 文/蒙蒙 一次员、第九天 我趴在偏房一處隱蔽的房頂上張望样眠。 院中可真熱鬧,春花似錦翠肘、人聲如沸檐束。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,755評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽被丧。三九已至盟戏,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間甥桂,已是汗流浹背柿究。 一陣腳步聲響...
    開封第一講書人閱讀 31,987評論 1 266
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留黄选,地道東北人蝇摸。 一個(gè)月前我還...
    沈念sama閱讀 46,358評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像办陷,于是被迫代替她去往敵國和親貌夕。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,514評論 2 348

推薦閱讀更多精彩內(nèi)容