Swift常用基礎(chǔ)知識(一)

匯編相關(guān)

lldb常用指令

//讀取寄存器的值
register read/格式
register read/

//修改寄存器的值
register write 寄存器名稱 數(shù)值
register write rax 0

//讀取內(nèi)存中的值
x/數(shù)量-格式-字節(jié)大小 內(nèi)存地址
x/3xw 0x0000010

//修改內(nèi)存中的值
memory write 內(nèi)存地址 數(shù)值
memory write 0x0000010 10

格式
x是16進(jìn)制蛙奖,f是浮點(diǎn)册倒,d是十進(jìn)制

字節(jié)大小
b-byte 1字節(jié)
h-half word 2字節(jié)
w-word 4字節(jié)
g-giant word 8字節(jié)

expreion 表達(dá)式
可以簡寫:expr 表達(dá)式
expreion $rax
expreion $rax = 1

po 表達(dá)式
print 表達(dá)式
po/x $rax
thread step-over筹误、next、n
單步運(yùn)行躯泰,把子函數(shù)當(dāng)做整體一步執(zhí)行(源碼級別)

thread step-in、step华糖、s
單步運(yùn)行麦向,遇到子函數(shù)會進(jìn)入子函數(shù)(源碼級別)

thread step-inst-over、nexti客叉、ni
單步運(yùn)行诵竭,把子函數(shù)當(dāng)做整體一步執(zhí)行(匯編級別)

thread step-inst话告、stepi、si
單步運(yùn)行卵慰,遇到子函數(shù)會進(jìn)入子函數(shù)(匯編級別)

thread step-out沙郭、finish
直接執(zhí)行完當(dāng)前函數(shù)的所有代碼,返回到上一個函數(shù)(遇到斷點(diǎn)會卡咨雅蟆)

規(guī)律

  • 內(nèi)存地址格式為:0x4bdc(%rip), 一般是全局變量
  • 內(nèi)存地址格式為:-0x78(%rbp), 一般是局部變量
  • 內(nèi)存地址格式為:-0x10(%rax), 一般是堆空間

寄存器

有16個常用寄存器

  • rax病线、rbx、rcx鲤嫡、rdx送挑、rsi、rdi泛范、rbp让虐、rsp
  • r8、r9罢荡、r10赡突、r11、r12区赵、r13惭缰、r14、r15

寄存器的具體用途

  • rax常作為函數(shù)返回值使用
  • rsi笼才、rdi漱受、rdx、rcx骡送、r8昂羡、r9等寄存器常用于存放函數(shù)參數(shù)
  • rsp、rbp用于棧操作
  • rip作為指令指針
    • 存儲著CPU下一條要執(zhí)行的指令地址
    • 一旦CPU讀取一條指令摔踱,rip會自動指向下一條指令地址(存儲下一條指令地址)

Swift語法

  • 被class修飾的計(jì)算類型屬性虐先,可以被子類重寫
  • 被static修飾的類型屬性(存儲、計(jì)算)派敷,不可以被子類重寫
class Circle {
    static var radius: Int = 0
    class var diameter: Int {
        set {
            print("Circle setDiameter")
            radius = newValue / 2
        }
        get {
            print("Circle getDiameter")
            return radius * 2
        }
    }
}

class SubCircle: Circle {
    override static var diameter: Int {
        set {
            print("Circle setDiameter")
            super.diameter = newValue>0 ? newValue : 0
        }
        get {
            print("Circle getDiameter")
            return super.diameter
        }
    }
}


Circle.radius = 6;
    // Circle getDiameter
    // 12
    print(Circle.diameter)
    //Circle setDiameter
    Circle.diameter = 20
    // 10
    print(Circle.radius)
    
    SubCircle.radius = 6
    //SubCircle getDiameter
    // Circle getDiameter
    //12
    print(SubCircle.diameter)
    //SubCircle setDiameter
    //Circle setDiameter
    SubCircle.diameter = 20
    //10
    print(SubCircle.radius)
    
內(nèi)存.png
//字符串長度<= 0xF蛹批,字符串內(nèi)容直接存放在str1變量的存中
var str1 = "0123456789"
//字符串長度> 0xF, 字符串內(nèi)容存放在_TEXT.cstring中 (常量區(qū)) 
//字符串的地址值信息存放在str2變量的后8個字節(jié)中
var str2 = "0123456789ABCDEF"
//由于字符串長度<= 0xF,所以字符串內(nèi)容依然存放在str1變量的內(nèi)存中
str1.append("ABCDE")
//開辟堆空間
str1.append("F")
//開辟堆空間
str2.append("G")

運(yùn)算符重載

struct Point {
    var x: Double = 0
    var y: Double = 0
    
    static func + (p1: Point, p2: Point) -> Point {
        Point(x: p1.x + p2.x, y: p1.y + p2.y)
    }
    static func - (p1: Point, p2: Point) -> Point {
        Point(x: p1.x - p2.x, y: p1.y - p2.y)
    }
    static prefix func - (p: Point) -> Point {
        Point(x: -p.x, y: -p.y)
    }
    static func += (p1: inout Point, p2: Point) {
        p1 = p1 + p2
    }
    static prefix func ++(p: inout Point) -> Point {
        p += Point(x: 1, y: 1)
        return p
    }
    static postfix func ++ (p: inout Point) -> Point {
        let tmp = p
        p += Point(x: 1, y: 1)
        return tmp
    }
    static func == (p1: Point, p2: Point) -> Bool {
        (p1.x == p2.x) && (p1.y == p2.y)
    }
}

計(jì)算屬性篮愉、下標(biāo)方法腐芍、嵌套類型

extension Double {
    var km: Double { self * 1_000.0 }
    var m: Double { self }
    var dm: Double { self / 10.0 }
    var cm: Double { self / 100.0 }
    var mm: Double { self / 1_000.0 }
}

extension Array {
    subscript(nullable idx: Int) -> Element? {
        if (startIndex..<endIndex).contains(idx) {
            return self[idx]
        }
        return nil
    }
}

extension Int {
    func repetitions(task: () -> Void) -> Void {
        for _ in 0..<self { task() }
    }
    
    mutating func square() -> Int {
        self = self * self
        return self
    }
    enum Kind { case negative, zero, postive }
    var kind: Kind {
        switch self {
        case 0: return .zero
        case let x where x > 0: return .postive
        default: return .negative
        }
    }
    subscript(digitIndex: Int) -> Int {
        var decimalBase = 1
        for _ in 0..< digitIndex { decimalBase *= 10 }
        return (self / decimalBase) % 10
    }
    
}

訪問控制( Access Control )

■ 在訪問權(quán)限控制這塊, Swift提供了5個不同的訪問級別(以下是從高到低排列,實(shí)體指被訪問級別修飾的內(nèi)容)

  • open :允許在定義實(shí)體的模塊试躏、其他模塊中訪問,允許其他模塊進(jìn)行繼承猪勇、重寫( open只能用在類、類成員上)
  • public :允許在定義實(shí)體的模塊颠蕴、其他模塊中訪問,不允許其他模塊進(jìn)行繼承埠对、重寫
  • internal :只允許在定義實(shí)體的模塊中訪問,不允許在其他模塊中訪問
  • fileprivate :只允許在定義實(shí)體的源文件中訪問
  • private :只允許在定義實(shí)體的封閉聲明中訪問

■ 絕大部分實(shí)體默認(rèn)都是internal級別

訪問級別的使用準(zhǔn)則

■ 一個實(shí)體不可以被更低訪問級別的實(shí)體定義,比如

  • 變量\常量類型 ≥ 變量\常量
  • 參數(shù)類型络断、返回值類型 ≥ 函數(shù)
  • 父類 ≥ 子類
  • 父協(xié)議 ≥子協(xié)議
  • 原類型 ≥ typealias
  • 原始值類型、關(guān)聯(lián)值類型 ≥ 枚舉類型
  • 定義類型A時用到的其他類型 ≥ 類型A

成員项玛、嵌套類型

■ 類型的訪問級別會影響成員(屬性貌笨、方法、初始化器襟沮、下標(biāo))锥惋、嵌套類型的默認(rèn)訪問級別

  • 一般情況下,類型為private或fileprivate ,那么成員\嵌套類型默認(rèn)也是private或fileprivate
  • 一般情況下,類型為internal或public ,那么成員\嵌套類型默認(rèn)是internal
public class PublicClass {
    public var p1 = 0
    var p2 = 0 // internal
    fileprivate func f1() {} // fileprivate
    private func f2() {} // private
}
class InternalClass { // internal
    var p = 0 // internal
    fileprivate func f1() {} // fileprivate
    private func f2() {} // private
}

fileprivate class FilePrivateClass { // fileprivate
    func f1() {} // fileprivate
    private func f2() {} // private

}
private class PrivateClass { // private
    func f() {} // private
}

■ 子類重寫的成員訪問級別必須 ≥ 父類的成員訪問級別

getter,setter

  • getter, setter默認(rèn)自動接收他們所屬環(huán)境的訪問級別

■ 可以給setter單獨(dú)設(shè)置一個比getter更低的訪問級別 ,用以限制寫的權(quán)限

fileprivate(set) public var num = 10
class Person {
    private(set) var age = 0
    fileprivate(set) public var weight: Int {
        set {}
        get { 10 }
    }
    internal(set) public subscript(index: Int) -> Int {
        set {}
        get { index }
    }
}

初始化器

■ 如果一個public類想在另一個模塊調(diào)用編譯生成的默認(rèn)無參初始化器,必須顯式提供public的無參初始化器

  • 因?yàn)?strong>public類的默認(rèn)初始化器是interna1級別

required初始化器必須跟它所屬類擁有相同的訪問級別
■如果結(jié)構(gòu)體有private\ fileprivate的存儲實(shí)例屬性,那么它的成員初始化器也是private\ fileprivate

  • 否則默認(rèn)就是internal

協(xié)議

■ 協(xié)議中定義的要求自動接收協(xié)議的訪問級別,不能單獨(dú)設(shè)置訪問級別

  • public協(xié)議定義的要求也是public

■ 協(xié)議實(shí)現(xiàn)的訪問級別必須 ≥ 類型的訪問級別,或者 ≥ 協(xié)議的訪問級別
■ 下面代碼能編譯通過么?

public protocol Runnable { 
    func run()
}
public class Person : Runnable {
    func run() {} 
}
//Method 'run()' must be declared public because it matches a requirement in public protocol 'Runnable'

擴(kuò)展

■ 在同一文件中的擴(kuò)展,可以寫成類似多個部分的類型聲明
口 在原本的聲明中聲明一個私有成員,可以在同一文件的擴(kuò)展中訪問它
口 在擴(kuò)展中聲明一個私有成員,可以在同一文件的其他擴(kuò)展中、原本聲明中訪問它

public class Person {
    private func run0() {}
    private func eat0() {
        run0()
    }
}
extension Person {
    private func run1() {}
    private func eat1() {
        run1()
    }
}
extension Person {
    private func eat2() {
        run1()
    }
}

閉包的循環(huán)引用

■ 如果想在定義閉包屬性的同時引用self,這個閉包必須是lazy的(因?yàn)樵趯?shí)例初始化完畢之后才能引用self)

■ 閉包fn內(nèi)部如果用到了實(shí)例成員(屬性开伏、方法)
編譯器會強(qiáng)制要求明確寫出self

class Person {
    lazy var fn: (()->()) = {
        [weak self] in
        self?.run()
    }
    func run() { print("run") }
    deinit { print ("deinit") }
}

■ 如果lazy屬性是閉包調(diào)用的結(jié)果,那么不用考慮循環(huán)引用的問題(因?yàn)殚]包調(diào)用后,閉包的生命周期就結(jié)束了)

class Person {
    var age: Int = 0
    lazy var getAge: Int = {
        self.age
    }()
    deinit { print("deinit") }
}

@escaping

■ 非逃逸閉包膀跌、逃逸閉包, 一般都是當(dāng)做參數(shù)傳遞給函數(shù)
■ 非逃逸閉包: 閉包調(diào)用發(fā)生在函數(shù)結(jié)束前,閉包調(diào)用在函數(shù)作用域內(nèi)
■ 逃逸閉包: 閉包有可能在函數(shù)結(jié)束后調(diào)用,閉包調(diào)用逃離了函數(shù)的作用域,需要通過@escaping聲明

import Dispatch
typealias Fn = () -> ()
// fn是非逃逸閉包
func test1(_ fn: Fn) { fn() }
// fn是逃逸閉包
var gFn: Fn?
func test2(_ fn: @escaping Fn) { gFn = fn }

■左邊的閉包fn內(nèi)部如果用到了實(shí)例成員(屬性、方法)

內(nèi)存訪問沖突 ( Conflicting Access to Memory )

■ 內(nèi)存訪問沖突會在兩個訪問滿足下列條件時發(fā)生:

  • 至少一個是寫入操作
  • 它們訪問的是同一塊內(nèi)存
  • 它們的訪問時間重疊(比如在同一個函數(shù)內(nèi))
 //不存在內(nèi)存訪問沖突
    func plus(_ num: inout Int) -> Int { num + 1 }
    var number = 1
    number = plus (&number)
    //存在內(nèi)存訪問沖突
    // Simultaneous accees to ?x0, but modificat ion requires exclusive acce
    var step = 1
    func increment(_ num:inout Int) { num += step }
    increment (&step)

    //解決內(nèi)存訪問沖突
    var copyOfStep = step
    increment(&copyOfStep)
    step = copyOfStep
func balance(_ x: inout Int,_ y: inout Int) {
        let sum=x+y
        x=sum/2
        y=sum-x
    }
    var num1 = 42
    var num2 = 30

    balance(&num1, &num2) // 0K
    balance(&num1, &num1) // Error
    
    
    struct Player {
        var name: String
        var health: Int
        var energy: Int
        mutating func shareHealth(with teammate: inout Player) {
            balance(&teammate.health, &health)
        }
    }
    var oscar = Player(name: "Oscar", health: 10, energy: 10)
    var maria = Player(name: "Maria", health: 5, energy: 10)
    
    oscar.shareHealth(with: &maria) // 0K
    oscar.shareHealth(with: &oscar) // Error
    
    
    var tulpe = (health: 10, energy: 20)
    // Error
    balance(&tulpe.health, &tulpe.energy)
    var holly = Player(name: "Holly", health: 10, energy: 10)
    // Error
    balance(&holly.health, &holly.energy)

■ 如果下面的條件可以滿足,就說明重疊訪問結(jié)構(gòu)體的屬性是安全的

  • 你只訪問實(shí)例存儲屬性,不是計(jì)算屬性或者類屬性
  • 結(jié)構(gòu)體是局部變量而非全局變量
  • 結(jié)構(gòu)體要么沒有被閉包捕獲要么只被非逃逸閉包捕獲
// OK
    func test() {
        var tulpe = (health: 10, energy: 20)
        balance (&tulpe.health, &tulpe.energy)
        var holly = Player(name: "Holly", health: 10, energy: 10)
        balance (&holly.health, &holly.energy)
    }
    test()

指針

■ Swift中也有專門的指針類型,這些都被定性為"Unsafe" (不安全的) , 常見的有以下4種類型

  • UnsafePointer<Pointee> 類似于 const Pointee *
  • UnsafeMutablePointer<Pointee> 類似于 Pointee *
  • UnsafeRawPointer 類似于 const void *
  • UnsafeMutableRawPointer 類似于 void *
    var age = 10
    func test1(_ ptr: UnsafeMutablePointer<Int>) {
        ptr.pointee += 10
    }
    func test2(_ ptr: UnsafePointer<Int>) {
        print(ptr.pointee)
    }
    
    test1(&age)
    test2(&age) // 20
    print(age) // 20
    var age = 10
    func test3(_ ptr: UnsafeMutableRawPointer) {
        ptr.storeBytes(of: 20, as: Int.self)
    }
    func test4(_ ptr: UnsafeRawPointer) {
        print(ptr.load(as: Int.self))
    }
    test3(&age)
    test4(&age) // 20
    print(age) // 20
    var age = 10
    var ptr = withUnsafePointer(to: &age) { (p) -> Int in
        return 20
    }

獲得某個變量的指針

    var age = 11
    var ptr1 = withUnsafeMutablePointer(to: &age) { $0 }
    var ptr2 = withUnsafePointer(to: &age) { $0 }
    ptr1.pointee = 22
    print(ptr2.pointee) // 22
    print(age) // 22
    var ptr3 = withUnsafeMutablePointer(to: &age) { UnsafeMutableRawPointer($0) }
    var ptr4 = withUnsafePointer(to: &age) { UnsafeRawPointer($0) }
    ptr3.storeBytes(of: 33, as: Int.self)
    print(ptr4.load(as: Int.self)) // 33
    print(age) // 33

創(chuàng)建指針

    var ptr1 = UnsafeRawPointer( bitPattern: 0x100001234)
    // 創(chuàng)建
    var ptr = malloc(16)
    // 存
    ptr?.storeBytes(of: 11, as: Int.self)
    ptr?.storeBytes(of: 22, toByteOffset: 8, as: Int.self)
    // 取
    print((ptr?.load(as: Int.self))!) // 11
    print((ptr?.load(fromByteOffset: 8, as: Int.self))!) // 22
    free(ptr)
    var ptr = UnsafeMutableRawPointer . allocate (byteCount: 16, alignment: 1)
    ptr.storeBytes(of: 11, as: Int.self)
    ptr.advanced(by: 8).storeBytes(of: 22, as: Int.self)
    print(ptr.load(as: Int.self)) // 11
    print(ptr.advanced(by: 8).load(as: Int.self)) // 22
    ptr.deallocate()

指針間的轉(zhuǎn)換

    var ptr = UnsafeMutableRawPointer . allocate(byteCount: 16, alignment: 1)
    ptr.aumingMemoryBound(to: Int.self).pointee = 11
    (ptr + 8).aumingMemoryBound(to: Double.self). pointee = 22.0
    print(unsafeBitCast(ptr, to: UnsafePointer<Int>.self).pointee) // 11
    print(unsafeBitCast(ptr + 8, to: UnsafePointer<Double>.self).pointee) // 22.0
    ptr.deallocate()
  • unsafeBitCast是忽略數(shù)據(jù)類型的強(qiáng)制轉(zhuǎn)換,不會因?yàn)閿?shù)據(jù)類型的變化而改變原來的內(nèi)存數(shù)據(jù)
  • 類似于C++ 中的reinterpret_cast
    class Person {}
    var person = Person()
    // personObjectAddre存儲的就是堆空間地址
    var personObjectAddre = unsafeBitCast(person, to: UInt.self)
    print(UnsafeRawPointer(bitPattern: personObjectAddre))
    var p = unsafeBitCast(person, to: UnsafeRawPointer.self)
    print(p)

字面量(Literal)

var age = 10
var isRed = false
var name = "Jack"

上面代碼中的10固灵、false, "Jack"就是字面量
■ 常見字面量的默認(rèn)類型

public typea lias IntegerLiteralType = Int
public typealias FloatLiteralType = Double
public typealias BooleanL iteralType = Bool
public typealias StringLiteralType = String
//可以通過typealias修改字 面量的默認(rèn)類型
typealias FloatLiteralType = Float
typealias IntegerLiteralType = UInt8
var age = 10 // UInt8
var height = 1.68 // Float

■ Swift自帶的絕大部分類型,都支持直接通過字面量進(jìn)行初始化
Bool捅伤、Int、Float巫玻、Double丛忆、String、Array仍秤、 Dictionary熄诡、 Set、Optional

字面量協(xié)議應(yīng)用

extension Int : ExpressibleByBooleanLiteral {
    public init( booleanLiteral value: Bool) { self = value ? 1 : 0 }
}
    var num: Int = true
    print(num) // 1
    //■有點(diǎn)類似于C+ +中的轉(zhuǎn)換構(gòu)造函數(shù)
    class Student : ExpressibleByIntegerLiteral, ExpressibleByFloatLiteral, ExpressibleByStringLiteral,
                    CustomStringConvertible {
    var name: String = ""
    var score: Double = 0
        required init(floatLiteral value: Double) { self.score = value }
        required init( integerLiteral value: Int) { self.score = Double(value) }
        required init(stringLiteral value: String) { self.name = value }
        required init ( unicodeScalarLiteral value: String) { self.name = value }
        required init (extendedGraphemeClusterLiteral value: String) { self.name = value }
        var description: String { "name=\(name) , score=\(score)"}
    }
    var stu: Student = 90
    print(stu) // name=, score=90.0
    stu = 98.5
    print(stu) // name=, score=98.5
    stu = "Jack"
    print(stu) // name=Jack, score=0.0
    
struct Point {
    var x = 0.0, y = 0.0
}
extension Point : ExpressibleByArrayLiteral, ExpressibleByDictionaryLiteral {
    init(arrayLiteral elements: Double...) {
        guard elements.count > 0 else { return }
        self.x = elements [0]
        guard elements.count > 1 else { return }
        self.y = elements [1]
    }
    init(dictionaryLiteral elements: (String, Double)...) {
        for (k, v) in elements {
            if k == "x" { self.x = v }
            else if k == "y" { self.y = v}
        }
    }
}
    var p: Point = [10.5, 20.5]
    print(p) // Point(x: 10.5, y: 20.5)
    p = ["x":11,"y":22]
    print(p) // Point(x: 11.0, y: 22.0)

通配符模式 ( Wildcard Pattern )

■ _ 匹配任何值
■ _?匹配非nil值

    enum Life {
        case human(name: String, age:Int?)
        case animal(name: String, age:Int?)
    }
    func check(_ life: Life) {
        switch life {
        case . human(let name, _) :
            print ("human", name)
        case . animal(let name,_?):
            print("animal", name)
        default:
            print("other")
        }
    }
    check( . human( name: "Rose", age: 20)) // human Rose
    check( . human(name: "Jack", age: nil)) // human Jack
    check( . animal(name: "Dog", age: 5)) // animal Dog
    check( . animal( name: "Cat", age: nil)) // other

枚舉Caset模式 ( Enumeration Case Pattern )

  • if case語句等價于只有1個case的switch語句
    let age=2
    //原來的寫法
    if age >= 0 && age <= 9 {
        print("[0, 9]")
    }
    //枚舉Case模式
    if case 0...9 = age {
        print("[0, 9]")
    }
    guard case 0...9 = age else { return }
    print("[0, 9]")
    
    switch age {
    case 0...9: print("[0, 9]")
    default: break
    }
    
    
    let ages: [Int?] = [2, 3, nil, 5]
    
    for case nil in ages {
        print("有nil")
        break
    } //有nil值
    
    let points = [(1, 0), (2, 1), (3, 0)]
    for case let(x, 0) in points {
        print(x)
    } // 1 3

可選模式 ( Optional Pattern )

    let age: Int? = 42
    if case .some(let x) = age { print(x) }
    if case let x? = age { print(x) }

    let ages: [Int?] = [nil, 2, 3, nil, 5]
    for case let age? in ages {
        print (age)
    }// 2 3 5

    for item in ages {
        if let age = item {
            print(age)
        } //跟上面的for,效果是等價的
    }
    
    func check(_ num: Int?) {
        switch num {
        case 2?: print("2")
        case 4?: print("4")
        case 6?: print("6")
        case _?: print("other")
        case _: print("nil")
        }
    }
    check(4) // 4
    check(8) // other
    check(nil) // nil

類型轉(zhuǎn)換模式 ( Type-Casting Pattern )

    let num: Any = 6
    switch num {
    case is Int:
        //編譯器依然認(rèn)為num是Any類型
        print("is Int", num)
//    case let n as Int:
//        print("as Int", n + 1)
    default:
        break
    }
    
    class Animal { func eat() { print(type(of: self), "eat") } }
    
    class Dog: Animal { func run() { print(type(of: self), "run") } }
    
    class Cat: Animal { func jump() { print(type(of: self), "jump") } }
    
    func check(_ animal: Animal) {
        
        switch animal {
        case let dog as Dog:
            dog.eat()
            dog.run()
        case is Cat:
            animal.eat()
        default: break
        }
    }
    
    // Dog eat
    // Dog run
    check(Dog())
    // Cat eat
    check(Cat())

表達(dá)式模式 ( Expression Pattern )

  • 表達(dá)式模式在case中
let point = (1, 2)
    switch point {
    case (0, 0):
        print("(0, 0) is at the origin.")
    case (-2...2, -2...2):
        print("(\(point.0), \(point.1)) is near the origin.")
    default:
        print("The point is at (\(point.0), \(point.1)).")
        
    }// (1, 2) is near the origin.

自定義表達(dá)式模式

■ 可以通過重載運(yùn)算符,自定義表達(dá)式模式的匹配規(guī)則

    struct Student {
        var score = 0, name = ""
        static func ~=(pattern: Int, value: Student) -> Bool { value.score >= pattern }
        static func ~=(pattern: ClosedRange<Int>, value: Student) -> Bool { pattern.contains(value.score) }
        static func ~=(pattern: Range<Int>, value: Student) -> Bool { pattern.contains (value.score) }
    }
    
    var stu = Student(score: 75, name: "Jack" )
    switch stu {
    case 100: print(">= 100")
    case 90: print(">= 90")
    case 80..<90: print("[80, 90)")
    case 60..79: print("[60, 79]")
    case 0: print(">= 0")
    default: break
    }// [60, 79]
    
    if case 60 = stu {
        print(">= 60")
    } //>= 60
    var info = (Student(score: 70, name: "Jack"), "及格")
    switch info {
    case let (60, text): print(text)
    default: break
    }//及格
extension String {
    static func ~= (pattern: (String) -> Bool, value: String) -> Bool {
        pattern(value)
    }
}
func hasPrefix(_ s: String) -> ((String) -> Bool) { { $0.hasPrefix(s) } }
func hasSuffix(_ s: String) -> ((String) -> Bool) { { $0.hasSuffix(s) } }


                                                      

    var str = "jack"
    switch str {
    case hasPrefix("j"), hasSuffix("k"):
        print("以j開頭诗力, 或以k結(jié)尾")
    default: break
    } // 以j開頭凰浮, 或以k結(jié)尾
func isEven(_ i: Int) -> Bool { i%2 == 0 }
func is0dd(_ i: Int) -> Bool { i%2 != 0 }
extension Int {
    static func ~= (pattern: (Int) -> Bool, value: Int) -> Bool {
        pattern( value )
    }
}
    var age = 10
    switch age {
    case isEven:
    print(age, "是個偶數(shù)" )
    case isOdd:
    print(age, "是個奇數(shù)")
    default: break
    }
prefix operator ~>
prefix operator ~>=
prefix operator ~<
prefix operator ~<=
prefix func ~> (_ i: Int) -> ((Int) -> Bool) { {$0 > i} }
prefix func ~>= (_ i: Int) -> ((Int) -> Bool) { {$0 >= i} }
prefix func ~< (_ i: Int) -> ((Int) -> Bool) { {$0 < i} }
prefix func ~<= (_ i: Int) -> ((Int) -> Bool) { {$0 <= i} }
    var age = 9
    switch age {
    case ~>=0, ~<=10:
    print("1")
    case ~>10, ~<20:
    print("2")
    default: break
    } // [0, 10]

Where

// 可以使用where為模式匹配增加匹配條件
    var data = (10, "Jack")
    switch data {
    case let (age, _) where age > 10:
        print(data.1, "age>10" )
    case let (age, _) where age > 0:
        print(data.1, "age>0")
    default: break
    }
    
    var ages = [10, 20, 44, 23, 55]
    for age in ages where age > 30 {
        print (age)
    }//44 55
    
    
protocol Stackable { associatedtype Element }
protocol Container {
    associatedtype Stack : Stackable where Stack.Element: Equatable
}

    func equal<S1: Stackable, S2: Stackable>(_ s1: S1,_ s2: S2) -> Bool where S1.Element == S2.Element, S1.Element: Hashable {
        return false
    }
    
extension Container where Self.Stack.Element: Hashable {}

條件編譯

// 操作系統(tǒng): macOS\i0S\tvOS\watchOS\Linux\Android\Windows\FreeBSD
#if os(macOS) || os(iOS)
// CPU#LMJ: i386x&6_ _64(a rma rm64
#elseif arch(x86_64) || arch(arm64 )
// swift版本
#elseif swift(<5) && swift(>=3)
// 模擬器
#elseif targetEnvironment(simulator)
// 可以導(dǎo)入某模塊
#elseif canImport(Foundation)
#else
#endif

// debug模式
#if DEBUG
// release模式
#else
    
#endif

#if TEST
    print("test")
#endif

#if OTHER
    print("other")
#endif
16602884931580.jpg

打印

func log<T>(_ msg: T ,
            file: NSString = #file,
            line: Int = #line,
            fn: String = #function) {
#if DEBUG
    let prefix = "\(file. lastPathComponent)_\(line)_\(fn):"
    print(prefix, msg)
#endif
}

系統(tǒng)版本檢測

if #available(iOS 10, macOS 10.12, *) {
    //對于i0S平臺,只在i0S10及以苇本,上版本執(zhí)行
    //對于mac0S平臺袜茧,只在mac0S 10. 12及以上版本執(zhí)行
    //最后的*表示在其他所有平臺都執(zhí)行
    }

API可用性說明

@available(iOS 10, macOS 10.15, *)
class PersonX {}
struct StudentX {
    @available(*, unavailable, renamed: "study" )
    func study_() {}
    func study() {}
    
    @available( iOS, deprecated: 11)
    @available (macOS, deprecated: 10.12)
    func run() {}
    
}

■ 更多用法參考

iOS程序的入口

//在AppDelegate上面默認(rèn)有個 @UIApplicationMain標(biāo)記 , 這表示
//編譯器自動生成入口代碼(main函數(shù)代碼),自動設(shè)置AppDelegate為APP的代理
//也可以刪掉 @UIApplicationMain , 自定義入口代碼 : 新建一個main.swift文件

import UIKit
class HYApplication : UIApplication {}
UIApplicationMain(CommandLine.argc,
                  CommandLine.unsafeArgv,
                  NSStringFromClass(HYApplication.self),
                  NSStringFromClass(AppDelegate.self))

Swift調(diào)用OC

新建1個橋接頭文件瓣窄,文件名格式默認(rèn)為:{targetName}-Bridging-Header.h


16602009689445.jpg

在{targetName}-Bridging-Header.h文件中#import "AEPerson.h" OC要暴露給Swift的內(nèi)容
注:Swift 項(xiàng)目創(chuàng)建OC文件時會提示你自動創(chuàng)建橋接頭文件

// OC中
int sum(int a, int b);
@interface AEPerson : NSObject


@property (nonatomic, assign) NSInteger age;
@property (nonatomic, copy) NSString *name;

- (instancetype)initWithAge:(NSInteger)age name:(NSString *)name;
+ (instancetype)personWithAge:(NSInteger)age name:(NSString *)name;

- (void)run;
+ (void)run;

- (void)eat:(NSString *)food other:(NSString *)other;
+ (void)eat:(NSString *)food other:(NSString *)other;
@end

@implementation AEPerson
- (instancetype)initWithAge:(NSInteger)age name:(NSString *)name {
    if (self = [super init]) {
        self.age = age;
        self.name = name;
    }
    return self;
}
+ (instancetype)personWithAge:(NSInteger)age name:(NSString *)name {
    return [[self alloc] initWithAge:age name:name];
}

+ (void)run { NSLog(@"Person +run"); }
- (void)run { NSLog(@"Person -run"); }

+ (void)eat:(NSString *)food other:(NSString *)other { NSLog(@"Person +eat %@ %@", food, other); }
- (void)eat:(NSString *)food other:(NSString *)other { NSLog(@"Person %zd %@ -eat %@ %@", (long)_age, _name, food, other); }

@end
int sum(int a, int b) { return a + b; }

// swift調(diào)OC

    var p = AEPerson(age: 10, name: "Jack" )
    p.age = 18
    p.name = "Rose"
    p.run() // 18 Rose -run
    p.eat("Apple", other: "Water") // 18 Rose -eat Apple Water
    AEPerson.run() // Person +run
    AEPerson.eat("Pizza", other: "Banana") // Person +eat Pizza Banana
    print(sum(10, 20)) // 30

Swift調(diào)用OC - @_silgen_name

  • 如果C語言暴露給Swift的凾數(shù)名跟Swift中的其他凾數(shù)名沖突了
  • 可以在Swift中使用 @_silgen_name 修改C函數(shù)名
// C語言
int sum(int a, int b) {}
    return a + b;
}


// Swift
@_silgen_name("sum") func swift_sum(_ v1: Int32, _ v2: Int32) -> Int32
func oc1() {
    print(swift_sum(10, 20)) // 30
    print(sum(10, 20)) // 30
}

OC調(diào)用Swift

  • Xcode已經(jīng)默認(rèn)生成一 個用于OC調(diào)用Swift的頭文件,文件名格式是: {targetName}-Swift.h
16602021727539.jpg

■ Swift暴露給OC的類最終繼承自NSObject
■ 使用@objc修飾需要暴露給OC的成員
■ 使用@objcMembers修飾類
口 代表默認(rèn)所有成員都會暴露給OC(包括擴(kuò)展中定義的成員)
口 最終是否成功暴露,還需要考慮成員自身的訪問級別

@objcMembers class Car: NSObject {
    var price: Double
    var band: String
    init(price: Double, band: String) {
        self.price = price
        self.band = band
    }
    func run() { print(price, band, "run") }
    static func run() { print("Car run") }
}
extension Car {
    func test() { print(price, band, "test") }
}

■可以通過@objc重命名Swift暴露給OC的符號名 (類名惫周、屬性名、函數(shù)名等)

@objc(AECar)
@objcMembers class Car: NSObject {
    var price: Double
    @objc(name)
    var band: String
    init(price: Double, band: String) {
        self.price = price
        self.band = band
    }
    @objc(drive)
    func run() { print(price, band, "run") }
    static func run() { print("Car run") }
}
extension Car {
    @objc(exec:v2:)
    func test(name: String, price: Int) { print(price, band, "test") }
}

注:在OC中引入頭文件#import "{targetName}-Swift.h"

    AECar *car = [[AECar alloc] initWithPrice:10.5 band:@"Bently"];
    car.name = @"BMW";
    car.price = 109;
    [car drive];
    
    [AECar run];

選擇器 ( Selector )

  • Swift中依然可以使用選擇器,使用#selector(name)定義一個選擇器
  • 必須是被@objcMembers或@objc修怖的方法オ可以定義選擇器
@objcMembers class Person2: NSObject {
    func test1(v1: Int) { print("test1") }
    func test2(v1: Int, v2: Int) { print("test2(v1:v2:)") }
    func test2(_ v1: Double,_ v2: Double) { print("test2(_ :_ _:)") }
    
    func run() {
        perform( #selector(test1))
        perform(#selector(test1(v1:)))
        perform(#selector(test2(v1:v2:)))
        perform(#selector(test2(_:_:)))
        perform(#selector(test2 as (Double, Double) -> Void))
    }
}

String

■Swift的字符串類型String ,跟OC的NSString ,在API設(shè)計(jì)上還是有較大差異

    //空字符串
    var emptyStr1 = ""
    var emptyStr2 = String()
    
    var str1 = "123456"
    print(str1.hasPrefix("123")) // true
    print(str1.hasSuffix("456")) // true
    
    var str: String = "1"
    // 拼接康栈, jack_rose
    str.append("_2")
    //重載運(yùn)算符+
    str = str + "_3"
    //重載運(yùn)算符+=
    str += "_4"
    // \()插值
    str = "\(str)_5"
    //長度,9喷橙,1_2_3_4_5
    print(str.count)

String的插入和刪除

    var str = "1_2"
    // 1_2_
    str.insert("_", at: str.endIndex)
    //1_2_3_4
    str.insert(contentsOf: "3_4", at: str.endIndex)
    // 1666_2_3_4
    str.insert(contentsOf: "666", at: str.index(after: str.startIndex))
    // 1666_2_3_8884
    str.insert(contentsOf: "888", at: str.index(before: str.endIndex) )
    // 1666hello_ 2_ _3_ 8884
    str.insert( contentsOf: "hello", at: str.index(str.startIndex, offsetBy: 4))

    var str = "1_2"
    //1_2_
    str.insert("_", at: str.endIndex)
    //1_2_3_4
    str.insert(contentsOf: "3_4", at: str.endIndex)
    // 1666_2_3_ 4
    str.insert(contentsOf: "666", at: str.index(after: str.startIndex) )
    // 1666_2_3_8884
    str.insert(contentsOf: "888", at: str.index(before: str.endIndex))
    // 1666hello_2_3_8884
    str.insert(contentsOf: "hello", at: str.index(str.startIndex, offsetBy: 4))
    
    // 666hello_2_3_8884
    str.remove(at: str.firstIndex(of: "1")!)
    // hello_2_3_8884
    str.removeAll { $0 == "6" }
    var range = str.index(str.endIndex, offsetBy: -4)..<str.index(before: str.endIndex)
    // hello_2_3_4
    str.removeSubrange(range)

Substring

Substring.jpg

// String可以通過下標(biāo), prefix啥么、suffix等截取子串,子串類型不是String ,而是Substring

    var str="1_2_3_4_5"
    // 1_2
    var substr1 = str.prefix(3)
    //4_5
    var substr2 = str.suffix(3)
    // 1_2
    var range = str.startIndex..<str.index(str.startIndex, offsetBy: 3)
    var substr3 = str[range]
    // 最初的String, 1_2_3_4_5
    print(substr3.base)
    // Substring -> String
    var str2 = String(substr3)
    // ■Substring和它的base ,共享字符串?dāng)?shù)據(jù)
    // Substring轉(zhuǎn)為String時, 會重新分配新的內(nèi)存存儲字符串?dāng)?shù)據(jù)

String與Character

for c in "jack" {// c是Character類型
        print(c)
    }
    var str = "jack"
    // c是Character類型
    var c = str[str.startIndex]

多行String

let str = """
1
    "2"
3
    '4'
"""
//縮進(jìn)以結(jié)尾的3引號為對齊線
let str9 = """
        1
            "2"
        3
            '4'
        """

#warning("要顯示3引號有問題啊")
// 如果要顯示3引號, 至少轉(zhuǎn)義1個引號
let str0 = """
Escaping the first quote \"""
Escaping two quotes \"\""
Escaping all three quotes \"\"\"
"""

//以下2個字符串是等價的
let str1 = "These are the same."
let str2 = """
These are the same.
"""

String 與 NSString

  • String 與 NSString之間可以隨時隨地橋接轉(zhuǎn)換
    -如果你覺得String的API過于復(fù)雜難用,可以考慮將String轉(zhuǎn)為NSString
var str10: String = "jack"
var str20: NSString = "rose"
var str3 = str1 as NSString
var str4 = str2 as String
// ja
var str5 = str3.substring (with: NSRange(location: 0, length: 2))
print(str5)
  • 比較字符串內(nèi)容是否等價
  • String使用 == 運(yùn)算符
  • NSString使用isEqual方法,也可以使用==返算符(本貭還是調(diào)用了isEqual方法)

Swift贰逾、 OC橋接轉(zhuǎn)換表

Swift OC
String ?? NSString
String ?? NSMutableString
Array ?? NSArray
Array ?? NSMutableArray
Dictionary ?? NSDictionary
Dictionary ?? NSMutableDictionary
Set ?? NSSet
Set ?? NSMutableSet

只能被class繼承的協(xié)議

protocol Runnable1: AnyObject {}
protocol Runnable2: class {}
@objc protocol Runnable3 {}
  • 被@objc修飾的協(xié)議悬荣,還可以暴露給OC取遵守實(shí)現(xiàn)

可選協(xié)議

■可以通過 @objc定義可選協(xié)議,這種協(xié)議只能被class遵守

@objc protocol Runnable {
    func run1()
    @objc optional func run2()
    func run3()
}
class Dog: Runnable {
    func run3() { print ("Dog run3") }
    func run1() { print("Dog run1") }
}
var d = Dog()
d.run1() // Dog run1
d.run3() // Dog run3

dynamic

■被 @objc dynamic 修怖的內(nèi)容會具有劫恣性,比如凋用方法會走runtime那一套流程

class Dog: NSObject {
    @objc dynamic func test1() {}
    func test2() {}
}
var d = Dog()
d.test1()
d.test2()

KVC\KVO

■Swift支持KVC\KVO的條件

  • 屬性所在的類、監(jiān)聽器最終繼承自NSObject
  • 用 @objc dynamic 修飾對應(yīng)的屬性
class Observer: NSObject {
    
    override func observeValue(forKeyPath keyPath: String?,
                               of object: Any?,
                               change: [NSKeyValueChangeKey : Any]?,
                               context: UnsafeMutableRawPointer?) {
        print ("observeValue", change?[.newKey] as Any)
    }
}
class Person: NSObject {
    @objc dynamic var age: Int = 0
    var observer: Observer = Observer()
    override init() {
        super.init( )
        self.addObserver(observer,
                          forKeyPath: "age",
                          options: .new,
                          context: nil)
    }
    deinit {
        self.removeObserver(observer,
                            forKeyPath: "age")
    }
}
var p = Person( )
// observeValue Optional(20)
p.age = 20
// observeValue Optional(25)
p.setValue(25, forKey: "age")

block方式的KVO

class Person: NSObject {
    @objc dynamic var age: Int = 0
    var observation: NSKeyValueObservation?
    override init() {
        super.init( )
        observation = observe(\Person.age, options: .new, changeHandler: { (person, change) in
            print(change.newValue as Any)
        })
    }
}
var p = Person( )
// Optional(20)
p.age = 20
//  Optional(25)
p.setValue(25, forKey: "age")

關(guān)聯(lián)對象( Associated Object )

//在Swift中, class依然可以使用關(guān)聯(lián)對象

  • 默認(rèn)情況, extension不可以增加存儲屬性
  • 借助關(guān)聯(lián)對象,可以實(shí)現(xiàn)類似extension為class增加存儲屬性的效果
class Person {}
extension Person {
    private static var AGE_KEY: Void?
    var age: Int {
        get {
            (objc_getAssociatedObject(self, &Self.AGE_KEY) as? Int) ?? 0
        }
        set {
            objc_setAssociatedObject(self, &Self.AGE_KEY, newValue, .OBJC_ASSOCIATION_ASSIGN)
        }
    }
}

資源名管理

enum R {
    enum string: String {
        case add = "添加"
    }
    enum image: String {
        case logo
    }
    enum segue: String {
        case login_main
    }
}
func testR() {
    let img = UIImage(named: "logo")
    let btn = UIButton(type: .custom)
    btn.setTitle("添加", for: .normal)
    performSegue(withIdentifier: "login_main", sender: self)
    
    let img = UIImage (R.image.logo)
    let btn = UIButton(type: .custom)
    btn.setTitle(R.string.add, for: .normal)
    performSegue ( withIdentifier: R.segue. login_main, sender: self)
}

//or
extension UIImage {
    convenience init?(_ name :R.image) {
        self.init(named: name.rawValue)
    }
}
extension UIViewController {
    func performSegue(withIdentifier identifier: R.segue, sender: Any?) {
        performSegue(withIdentifier: identifier .rawValue, sender: sender)
    }
}
    
extension UIButton {
    func setTitle(_ title: R.string, for state: UIControl.State) {
        setTitle(title.rawValue, for: state)
    }
}

■這種做法實(shí)際上是參考了Android的資源名管理方式

資源名管理的其他思路

let img = UIImage(named: "logo")
let font = UIFont(name: "Arial", size: 14)
let img = R.image.logo
let font = R.font.arial(14)
enum R {
    enum image {
        static var logo = UIImage (named: "logo")
    }
    enum font {
        
        static func arial(_ size: CGFloat) -> UIFont? {
            UIFont(name: "Arial", size: size)
        }
    }
}

更多優(yōu)秀思路 參考

多線程開發(fā)

異步

public typealias Task = () -> Void
struct Queue {
    public static func async(_ task: @escaping Task) {
        _async(task)
    }
    public static func async(_ task: @escaping Task,
                             mainTask: @escaping Task) {
        _async(task, mainTask)
    }
    
    private static func _async(_ task: @escaping Task,
                               _ mainTask: Task? = nil) {
        let item = DispatchWorkItem( block: task)
        DispatchQueue.global().async(execute: item)
        if let main = mainTask {
            item.notify(queue: DispatchQueue.main, execute: main)
        }
    }
}

延遲

    @discardableResult
    public static func delay(_ seconds: Double, _ block: @escaping Task) -> DispatchWorkItem {
        let item = DispatchWorkItem(block: block)
        DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + seconds, execute: item)
        return item
    }

異步延遲

    @discardableResult
    public static func asyncDelay(_ seconds: Double ,
                                  _ task: @escaping Task) -> DispatchWorkItem {
        return _asyncDelay(seconds, task)
    }
    @discardableResult
    public static func asyncDelay(_ seconds: Double,
                                  _ task: @escaping Task,
                                  _ mainTask: @escaping Task) -> DispatchWorkItem {
        return _asyncDelay(seconds, task, mainTask)
    }
    
    private static func _asyncDelay(_ seconds: Double,
                                    _ task: @escaping Task,
                                    _ mainTask: Task? = nil) -> DispatchWorkItem {
    let item = DispatchWorkItem(block: task)
        DispatchQueue.global().asyncAfter(deadline: DispatchTime.now() + seconds,
                                          execute: item)
        if let main = mainTask {
            item.notify(queue: DispatchQueue.main, execute: main)
        }
        return item
    }

once

■ dispatch_once在Swift中已被廢棄,取而代之

  • 可以用類型屬性或者全局變量\常量
  • 默認(rèn)自帶lazy + dispatch_once效果
let age1: Int = {
print( 666)
return 10
}()
class ViewController: UIViewController {
    static let age2: Int = {
        print (888)
        return 20
    }()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        print (age1)
        print(age1)
        // 666 10 10
        print (ViewController.age2)
        print (ViewController.age2)
        // 888 20 20
    }
}

加鎖

//gcd信號量

class Cache {
    private static var data = [String: Any]()
    private static var lock = DispatchSemaphore(value: 1)
    
    static func set(_ key: String, value: Any) {
        lock.wait()
        defer { lock.signal() }
        data [key] = value
    }
}
//Foundation
    private static var lock = NSLock()
    static func set(_ key: String,_ value: Any) {
        lock.lock()
        defer { lock.unlock() }
    }
private static var lock = NSRecursiveLock()
    static func set(_ key: String,_ value: Any) {
        lock.lock()
        defer { lock.unlock() }
    }

Swift常用基礎(chǔ)知識(二)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末疙剑,一起剝皮案震驚了整個濱河市氯迂,隨后出現(xiàn)的幾起案子践叠,更是在濱河造成了極大的恐慌,老刑警劉巖嚼蚀,帶你破解...
    沈念sama閱讀 216,692評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件禁灼,死亡現(xiàn)場離奇詭異,居然都是意外死亡轿曙,警方通過查閱死者的電腦和手機(jī)弄捕,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,482評論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來导帝,“玉大人守谓,你說我怎么就攤上這事∧ィ” “怎么了斋荞?”我有些...
    開封第一講書人閱讀 162,995評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長虐秦。 經(jīng)常有香客問我平酿,道長,這世上最難降的妖魔是什么羡疗? 我笑而不...
    開封第一講書人閱讀 58,223評論 1 292
  • 正文 為了忘掉前任染服,我火速辦了婚禮,結(jié)果婚禮上叨恨,老公的妹妹穿的比我還像新娘柳刮。我一直安慰自己,他們只是感情好痒钝,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,245評論 6 388
  • 文/花漫 我一把揭開白布秉颗。 她就那樣靜靜地躺著,像睡著了一般送矩。 火紅的嫁衣襯著肌膚如雪蚕甥。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,208評論 1 299
  • 那天栋荸,我揣著相機(jī)與錄音菇怀,去河邊找鬼。 笑死晌块,一個胖子當(dāng)著我的面吹牛爱沟,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播匆背,決...
    沈念sama閱讀 40,091評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼呼伸,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了钝尸?” 一聲冷哼從身側(cè)響起括享,我...
    開封第一講書人閱讀 38,929評論 0 274
  • 序言:老撾萬榮一對情侶失蹤搂根,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后铃辖,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體剩愧,經(jīng)...
    沈念sama閱讀 45,346評論 1 311
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,570評論 2 333
  • 正文 我和宋清朗相戀三年澳叉,在試婚紗的時候發(fā)現(xiàn)自己被綠了隙咸。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,739評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡成洗,死狀恐怖五督,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情瓶殃,我是刑警寧澤充包,帶...
    沈念sama閱讀 35,437評論 5 344
  • 正文 年R本政府宣布,位于F島的核電站遥椿,受9級特大地震影響基矮,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜冠场,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,037評論 3 326
  • 文/蒙蒙 一家浇、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧碴裙,春花似錦钢悲、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,677評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至载慈,卻和暖如春惭等,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背办铡。 一陣腳步聲響...
    開封第一講書人閱讀 32,833評論 1 269
  • 我被黑心中介騙來泰國打工辞做, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人寡具。 一個月前我還...
    沈念sama閱讀 47,760評論 2 369
  • 正文 我出身青樓秤茅,卻偏偏與公主長得像,于是被迫代替她去往敵國和親晒杈。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,647評論 2 354

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