swift 指針學(xué)習(xí)記錄

參考文獻(xiàn)

Swift結(jié)構(gòu)體指針操作
官方文檔
Swift 和 C 不得不說的故事
Swift指針和托管穿扳,你看我就夠了

Why?

swift中并不推薦對指針直接操作蹬屹,但畢竟iOS中有的庫還是使用C語言構(gòu)建的扮碧,在與C混編的時候磁椒,不可避免的要將對象轉(zhuǎn)換為C的指針類型以便傳值堤瘤。比如GCD,CF框架Runloop操作。

各種指針

對應(yīng)表

下面表中Type為類型占位浆熔,可替換為int等等

對于返回值本辐、變量、參數(shù)的指針:

C Swift example
const Type * UnsafePointer<Type> const int * 轉(zhuǎn)換為UnsafePointer <Int32>
Type * UnsafeMutablePointer<Type> int * 轉(zhuǎn)換為UnsafeMutablePointer <Int32>

對于類對象的指針

C Swift
Type * const * UnsafePointer<Type>
Type * __strong * UnsafeMutablePointer<Type>
Type ** AutoreleasingUnsafeMutablePointer<Type>

對于無符號類型医增、

C Swift
const void * UnsafeRawPointer
void * UnsafeMutableRawPointer

如果像不完整結(jié)構(gòu)體的這樣的c指針的值的類型無法用Swift來表示慎皱,則用OpaquePointer來表示

常量指針

定義為 UnsafePointer<Type> 的指針為常量指針,在C中有const修飾叶骨。

當(dāng)函數(shù)的參數(shù)修飾為 UnsafePointer<Type>的時候茫多,可以接收下面幾種類型的值:

  • 類型為UnsafePointer<Type>, UnsafeMutablePointer<Type>, or AutoreleasingUnsafeMutablePointer<Type>或者轉(zhuǎn)換為UnsafePointer<Type>類型的值。
  • String類型的值忽刽,如果類型是Int8UInt8天揖,那字符串會自動轉(zhuǎn)換為UTF8的buffer,然后這個buffer的指針將會傳入函數(shù)跪帝。
  • 表達(dá)式Type類型的變量今膊、屬性、同類型的下標(biāo)表達(dá)(如:a[0])伞剑,這樣的情況下斑唬,將左邊起始地址傳入函數(shù)。
  • [Type]數(shù)組類型值黎泣,將數(shù)組起始地址傳入函數(shù)恕刘。

在調(diào)用函數(shù)時,必須保證傳入的值可用抒倚。

例子:

func takesAPointer(_ p: UnsafePointer<Float>) {
    // ...
}

var x: Float = 0.0
takesAPointer(&x)
takesAPointer([1.0, 2.0, 3.0])

上面的例子中必須定義時必須指定一個類型雪营,如:Float『獗悖可以使用UnsafeRawPointer修飾献起,則可以傳入相同類型的Type洋访。

例子:

func takesARawPointer(_ p: UnsafeRawPointer?)  {
    // ...
}

var x: Float = 0.0, y: Int = 0
takesARawPointer(&x)
takesARawPointer(&y)
takesARawPointer([1.0, 2.0, 3.0] as [Float])
let intArray = [1, 2, 3]
takesARawPointer(intArray)

可變指針

定義為UnsafeMutablePointer<Type>、UnsafeMutableRawPointer的指針為可變指針谴餐,可以接收下面幾種類型的值:

  • 類型為UnsafeMutablePointer<Type>類型的值姻政。
  • 表達(dá)式Type類型的變量、屬性岂嗓、同類型的下標(biāo)表達(dá)(如:a[0])汁展,這樣的情況下,將地址傳入函數(shù)厌殉。
  • 表達(dá)式[Type]類型的變量食绿、屬性、同類型的下標(biāo)表達(dá)公罕,這樣的情況下器紧,將左邊起始地址傳入函數(shù)。
func takesAMutablePointer(_ p: UnsafeMutablePointer<Float>) {
    // ...
}

var x: Float = 0.0
var a: [Float] = [1.0, 2.0, 3.0]
takesAMutablePointer(&x)
takesAMutablePointer(&a)

// 不指定具體類型
func takesAMutableRawPointer(_ p: UnsafeMutableRawPointer?)  {
    // ...
}

var x: Float = 0.0, y: Int = 0
var a: [Float] = [1.0, 2.0, 3.0], b: [Int] = [1, 2, 3]
takesAMutableRawPointer(&x)
takesAMutableRawPointer(&y)
takesAMutableRawPointer(&a)
takesAMutableRawPointer(&b)

Autoreleasing Pointers(自動釋放指針)

定義為AutoreleasingUnsafeMutablePointer<Type>楼眷,可以接收下面幾種類型的值:

  • AutoreleasingUnsafeMutablePointer<Type>的值
  • 表達(dá)式Type類型的變量铲汪、屬性、同類型的下標(biāo)表達(dá)(如:a[0])罐柳,這里會按位拷貝到臨時緩沖區(qū)掌腰,緩沖區(qū)的值會被加載,retain张吉,分配值齿梁。

注意:不能傳數(shù)組

例子:

func takesAnAutoreleasingPointer(_ p: AutoreleasingUnsafeMutablePointer<NSDate?>) {
    // ...
}

var x: NSDate? = nil
takesAnAutoreleasingPointer(&x)

函數(shù)指針

在C中有回調(diào)函數(shù),當(dāng)swift要調(diào)用C中這類函數(shù)時肮蛹,可以使用函數(shù)指針士飒。

swift中可以用@convention 修飾一個閉包,

  • @convention(swift) : 表明這個是一個swift的閉包
  • @convention(block) :表明這個是一個兼容oc的block的閉包蔗崎,可以傳入OC的方法酵幕。
  • @convention(c) : 表明這個是兼容c的函數(shù)指針的閉包,可以傳入C的方法缓苛。

C中的方法int (*)(void) 在swift中就是@convention(c) () -> Int32

在調(diào)用C函數(shù)需要傳入函數(shù)指針時芳撒,swift可以傳入閉包的字面量或者nil,也可以直接傳入一個閉包未桥。

例如:

func customCopyDescription(_ p: UnsafeRawPointer?) -> Unmanaged<CFString>? {
    // return an Unmanaged<CFString>? value
}
 
var callbacks = CFArrayCallBacks(
    version: 0,
    retain: nil,
    release: nil,
    copyDescription: customCopyDescription,
    equal: { (p1, p2) -> DarwinBoolean in
        // return Bool value
}
)
 
var mutableArray = CFArrayCreateMutable(nil, 0, &callbacks)

上面的例子中笔刹,retain、release是傳入的nil冬耿,copyDescription是傳入的函數(shù)字面量舌菜,equal是直接傳入的閉包。

Buffer Pointers

buffer指針用于比較底層的內(nèi)存操作亦镶,你可以使用buffer指針做高效的處理和應(yīng)用程序與服務(wù)間的通信日月。

有下面幾種類型的buffer指針

  • UnsafeBufferPointer
  • UnsafeMutableBufferPointer
  • UnsafeRawBufferPointer
  • UnsafeMutableRawBufferPointer

UnsafeBufferPointer袱瓮、 UnsafeMutableBufferPointer,能讓你查看或更改一個連續(xù)的內(nèi)存塊爱咬。

UnsafeRawBufferPointer尺借、UnsafeMutableRawBufferPointer能讓你查看或更改一個連續(xù)內(nèi)存塊的集合,集合里面每個值對應(yīng)一個字節(jié)的內(nèi)存精拟。

指針用法

當(dāng)使用指針實例的時候燎斩,可以使用pointee屬性獲取指針指向內(nèi)容的值,指針不會自動管理內(nèi)存或?qū)?zhǔn)擔(dān)保蜂绎。你必須自己管理生命周期以避免泄露和未定義的行為栅表。

內(nèi)存可能有幾種狀態(tài):未指定類型未初始化、指定類型未初始化师枣、指定類型已初始化怪瓶。

  • 未分配的:沒有預(yù)留的內(nèi)存分配給指針
  • 已分配的:指針指向一個有效的已分配的內(nèi)存地址,但是值沒有被初始化坛吁。
  • 已初始化:指針指向已分配和已初始化的內(nèi)存地址。

指針將根據(jù)我們具體的操作在這 3 個狀態(tài)之間進(jìn)行轉(zhuǎn)換铐尚。

用法示例

未分配的指針用allocate方法分配一定的內(nèi)存空間拨脉。

 let uint8Pointer = UnsafeMutablePointer<UInt8>.allocate(capacity: 8)

分配完內(nèi)存空間的指針用各種init方法來綁定一個值或一系列值。初始化時宣增,必須保證指針是未初始化的玫膀。(初始化過的指針可以再次調(diào)用初始化方法不會報錯,所以使用時需要特別注意爹脾。)

uint8Pointer.initialize(to: 20, count: 8)
print(uint8Pointer[0])  // 20

然后修改值

uint8Pointer[0] = 10
print(uint8Pointer[0])  // 10

回到初始化值之前帖旨,沒有釋放指針指向的內(nèi)存,指針依舊指向之前的值灵妨。

uint8Pointer.deinitialize(count: 8)

釋放指針指向的內(nèi)存,據(jù)官方文檔說解阅,在釋放指針內(nèi)存之前,必須要保證指針是未初始化的泌霍,不然會產(chǎn)生問題货抄。

uint8Pointer.deallocate(capacity: 8)
print(uint8Pointer[0])  // 可能是任何值,已經(jīng)銷毀了

其他用法

看個栗子:

struct MyStruct1{
    var int1:Int
    var int2:Int
}

var s1ptr = UnsafeMutablePointer<MyStruct1>.allocate(capacity: 5)

s1ptr[0] = MyStruct1(int1: 1, int2: 2)
s1ptr[1] = MyStruct1(int1: 1, int2: 2) // 似乎不應(yīng)該是這樣朱转,但是這能夠正常工作

s1ptr.deinitialize(count: 5)
s1ptr.deallocate(capacity: 5)

這個栗子中沒有使用init方法來初始化指針蟹地,但是可以正常工作。不過這樣寫并不推薦藤为,它不適用指針指向一個類怪与,或某些特定的結(jié)構(gòu)體和枚舉的情況。

why缅疟?

當(dāng)你使用上面提及的方式修改內(nèi)存內(nèi)容分别,從內(nèi)存管理角度來說遍愿,有關(guān)這種行為背后的原因和發(fā)生時有關(guān)的。讓我們來看一個不需要手動初始化內(nèi)存的代碼片段茎杂,倘若我們在沒有初始化 UnsafePointer 情況下改變了指針指向的內(nèi)存错览,會引發(fā)崩潰。

class TestClass{
    var aField:Int = 0
}

struct MyStruct2{
    var int1:Int
    var int2:Int
    var tc:TestClass // 這個字段是引用類型
}

var s2ptr = UnsafeMutablePointer<MyStruct2>.allocate(capacity: 5)

s2ptr.initialize(to: MyStruct2(int1: 1, int2: 2, tc: TestClass()), count: 2) // 刪除這行初始化代碼將引發(fā)崩潰

s2ptr[0] = MyStruct2(int1: 1, int2: 2, tc: TestClass())
s2ptr[1] = MyStruct2(int1: 1, int2: 2, tc: TestClass())

s2ptr.deinitialize(count: 5)
s2ptr.deallocate(capacity: 5)

MyStruct2 包含一個引用類型煌往,所以它的生命周期交由 ARC 管理倾哺。當(dāng)我們修改其中一個指向的內(nèi)存模塊值的時候,Swift 運(yùn)行時將試圖釋放之前存在的對象刽脖,由于這個對象沒有被初始化羞海,內(nèi)存存在垃圾,你的應(yīng)用將會崩潰曲管。

請牢記這一點却邓,從安全的角度來講,最受歡迎的初始化手段是使用 initialize 分配完成內(nèi)存后院水,直接設(shè)置變量的初始值腊徙。

轉(zhuǎn)換

可變 不可變

當(dāng)一個函數(shù)需要傳入不可變指針時,可變指針可以直接傳入檬某。

而當(dāng)一個函數(shù)需要可變指針時撬腾,可以使用init(mutating other: UnsafePointer<Pointee>)方法轉(zhuǎn)換

var i: Int8 = 12
func printPointer(p: UnsafePointer<Int8>) {
    let muS2ptr = UnsafeMutablePointer<Int8>.init(mutating: p)!
    print(muS2ptr.pointee)
}
printPointer(p: &i)

各種類型轉(zhuǎn)換:

var i: Int8 = 12
func printPointer(p: UnsafePointer<Int8>) {
    let muS2ptr = UnsafeMutablePointer<Int8>.init(mutating: p)!  //  UnsafePointer<Int8> -> UnsafeMutablePointer<Int8>
    print(muS2ptr.pointee)
    
    var constUnTypePointer = UnsafeRawPointer(p) // UnsafePointer<Int8> - > UnsafeRawPointer
    var unTypePointer = UnsafeMutableRawPointer(mutating: constUnTypePointer) // UnsafeRawPointer -> UnsafeMutableRawPointer
    var unTypePointer2 = UnsafeMutableRawPointer(muS2ptr) // UnsafeMutablePointer<Int8> ->  UnsafeMutableRawPointer
    
}

指針可以使用load等方法轉(zhuǎn)為對應(yīng)的類型

func print<T>(address p: UnsafeRawPointer, as type: T.Type) {
    let value = p.load(as: type)
    print(value)
}

不同類型

下面的例子展示了將Uint8的指針 轉(zhuǎn)換為UInt64類型。

var i: UInt8 = 125
func printPointer(uint8Pointer: UnsafePointer<UInt8>) {
    
    let pointer0 = UnsafeRawPointer(uint8Pointer)
        .bindMemory(to: UInt64.self, capacity: 1)
    let pointer0Value = pointer0.pointee
    print(pointer0Value) // UInt64
    
    let pointer1 = UnsafeRawPointer(uint8Pointer).assumingMemoryBound(to: UInt64.self)
    let pointer1Value = pointer1.pointee
    print(pointer1Value) // UInt64
    
    let pointer2Value = UnsafeRawPointer(uint8Pointer).load(as: UInt64.self)
    print(pointer2Value) // UInt64
    
    let pointer3 = UnsafeMutablePointer(mutating: uint8Pointer).withMemoryRebound(to: UInt64.self, capacity: 1) { return $0 }
    print(pointer3.pointee) // UInt64
}
printPointer(uint8Pointer: &i)

對象轉(zhuǎn)換

在調(diào)用一些底層庫的時候恢恼,經(jīng)常要把Swift對象傳入C中民傻,然后將從C中的回調(diào)函數(shù)中轉(zhuǎn)換成Swift對象。

下面是我自己寫的一個例子:

定義的C代碼是:

// c的頭文件
#include <stdio.h>

typedef struct {
    void *info;
    const void *(*retain)(const void *info);
}Context;

void abcPrint(Context *info, void (*callback)(void *));
    
// c 的實現(xiàn)文件
void abcPrint(Context *info, void (*callback)(void *)){
    
    (*callback)(info->info);
    printf("abcPrint call");
}

上面的代碼context中有個info可以帶void *對象场斑, 然后在回調(diào)方法中將info對象返回回來漓踢。

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
        
        var blockSelf = self
        let controllerPoint = withUnsafeMutablePointer(to: &blockSelf) { return $0}
        print("\(self)")
        
        var context = Context(info: controllerPoint, retain: nil)
        
        abcPrint(&context) {
            let controller1 = $0?.assumingMemoryBound(to: ViewController.self).pointee
            print("controller1: \(String(describing: controller1))")
            
            let controller2 = $0?.bindMemory(to: ViewController.self, capacity: 1).pointee
            print("controller2: \(String(describing: controller2))")
            
            let controller3 = $0?.load(as: ViewController.self)
            print("controller3: \(String(describing: controller3))")
        }
    }
}

withUnsafeMutablePointer方法可以將Swift對象ViewController轉(zhuǎn)換為UnsafeMutablePointer<ViewController>類型,這樣才可以當(dāng)做參數(shù)傳入C函數(shù)漏隐。

C函數(shù)的回調(diào)函數(shù)中喧半,傳出來一個UnsafeMutableRawPointer對象的指針,我展示了3種方式青责,可以將這個指針轉(zhuǎn)換為ViewController對象薯酝。

CFRoopLoop使用

相信有的人看到上面的代碼,知道我為什么會寫這個知識點了爽柒。因為我在使用CFRunLoop優(yōu)化大圖加載的時候遇到了這樣的一個需求吴菠。

當(dāng)創(chuàng)建CFRunLoopObserverContext時需要傳入Swift對象到info屬性,在CFRunLoopObserverCreate的回調(diào)函數(shù)中浩村,會把這個info返回回來使用做葵。這里就需要這樣的類型轉(zhuǎn)換。

Unmanaged方式

蘋果的一些底層框架返回的對象有的是自動管理內(nèi)存的(annotated APIs)心墅,有的是不自動管理內(nèi)存酿矢。

  • 對于Core Fundation中有@annotated注釋的函數(shù)來說榨乎,返回的是托管對象,無需自己管理內(nèi)存瘫筐,可以直接獲取到CF對象蜜暑,并且可以無縫轉(zhuǎn)化(toll free bridging)成Fundation對象,比如NSString和CFString策肝。目前肛捍,內(nèi)存管理注釋正在一步步的完善,所以等到未來某一個版本我們就可以完完全全的像使用Fundation一樣使用Core Fundation啦之众。

  • 對于尚未注釋的函數(shù)來說拙毫,蘋果給出的是使用非托管對象Unmanaged<T>進(jìn)行管理的過渡方案。
    當(dāng)我們從CF函數(shù)中獲取到Unmanaged<T>對象的時候棺禾,我們需要調(diào)用takeRetainedValue或者takeUnretainedValue獲取到對象T缀蹄。具體使用哪一個方法,蘋果提出了Ownership Policy膘婶,具體來說就是:

    1. 如果一個函數(shù)名中包含CreateCopy缺前,則調(diào)用者獲得這個對象的同時也獲得對象所有權(quán),返回值Unmanaged需要調(diào)用takeRetainedValue()方法獲得對象悬襟。調(diào)用者不再使用對象時候衅码,Swift代碼中不需要調(diào)用CFRelease函數(shù)放棄對象所有權(quán),這是因為Swift僅支持ARC內(nèi)存管理古胆,這一點和OC略有不同肆良。
    2. 如果一個函數(shù)名中包含Get筛璧,則調(diào)用者獲得這個對象的同時不會獲得對象所有權(quán)逸绎,返回值Unmanaged需要調(diào)用takeUnretainedValue()方法獲得對象。

下面是我自己的試驗的代碼:

func addRunloopOberver1() {
        
        let controllerPoint = Unmanaged<ViewController>.passUnretained(self).toOpaque()
        
        var content = CFRunLoopObserverContext(version: 0, info: controllerPoint, retain: nil, release: nil, copyDescription: nil)
        
        runloopObserver = CFRunLoopObserverCreate(nil, CFRunLoopActivity.beforeWaiting.rawValue, true, 0, { (oberver, activity, info) in
            
            if info == nil {//如果沒有取到  直接返回
                return
            }
            
            let controller = Unmanaged<ViewController>.fromOpaque(info!).takeUnretainedValue()
            
            
            if controller.isKind(of: PicturesDetailViewController.self) {
                controller.runloopCall()
            }
            
            
        }, &content)
        
        CFRunLoopAddObserver(runloop, runloopObserver, CFRunLoopMode.commonModes)
    }

注意: 如果這里使用takeRetainedValue 夭谤、passRetained方法棺牧,會崩潰,我理解的是標(biāo)明為retain的值朗儒,在使用后ARC會自動將它release一次颊乘,這樣在某個時候self對象就會被釋放了,當(dāng)runloop再次用到self的時候就會崩潰醉锄。如果用unretain的值乏悄,ARC就不會去retain和release這個指針對象。

不建議的方式

看下面代碼:

func addRunloopOberver() {
        
        let controllerPoint = unsafeBitCast(self, to: UnsafeMutableRawPointer.self)
        
        var content = CFRunLoopObserverContext(version: 0, info: controllerPoint, retain: nil, release: nil, copyDescription: nil)
        
        runloopObserver = CFRunLoopObserverCreate(nil, CFRunLoopActivity.beforeWaiting.rawValue, true, 0, { (oberver, activity, info) in
            
            if info == nil {//如果沒有取到  直接返回
                return
            }
            
            let controller = unsafeBitCast(info, to: PicturesDetailViewController.self)
            
            if controller.isKind(of: ViewController.self) {
                controller.runloopCall()
            }
            
            
        }, &content)
        
        CFRunLoopAddObserver(runloop, runloopObserver, CFRunLoopMode.commonModes)
    }

上面使用了unsafeBitCast方法恳不,強(qiáng)行將self對象轉(zhuǎn)換為指針檩小,最后也是強(qiáng)行轉(zhuǎn)換回來。雖然說也可以運(yùn)行烟勋,但是官方文檔中建議规求,不到萬不得已不要使用這個方法筐付,特別危險。阻肿。

下面是官方文檔原文
Use this function only to convert the instance passed as x to a layout-compatible type when conversion through other means is not possible. Common conversions supported by the Swift standard library include the following:

Warning
Calling this function breaks the guarantees of the Swift type system; use with extreme care.

  • Value conversion from one integer type to another. Use the destination type’s initializer or the numericCast(_:) function.
  • Bitwise conversion from one integer type to another. Use the destination type’s init(truncatingIfNeeded:) or init(bitPattern:) initializer.
  • Conversion from a pointer to an integer value with the bit pattern of the pointer’s address in memory, or vice versa. Use the init(bitPattern:) initializer for the destination type.
  • Casting an instance of a reference type. Use the casting operators (as, as!, or as?) or the unsafeDowncast(:to:) function. Do not use unsafeBitCast(:to:) with class or pointer types; doing so may introduce undefined behavior.
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末瓦戚,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子丛塌,更是在濱河造成了極大的恐慌较解,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,378評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件姨伤,死亡現(xiàn)場離奇詭異哨坪,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)乍楚,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,356評論 2 382
  • 文/潘曉璐 我一進(jìn)店門当编,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人徒溪,你說我怎么就攤上這事忿偷。” “怎么了臊泌?”我有些...
    開封第一講書人閱讀 152,702評論 0 342
  • 文/不壞的土叔 我叫張陵鲤桥,是天一觀的道長。 經(jīng)常有香客問我渠概,道長茶凳,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,259評論 1 279
  • 正文 為了忘掉前任播揪,我火速辦了婚禮贮喧,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘猪狈。我一直安慰自己箱沦,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 64,263評論 5 371
  • 文/花漫 我一把揭開白布雇庙。 她就那樣靜靜地躺著谓形,像睡著了一般。 火紅的嫁衣襯著肌膚如雪疆前。 梳的紋絲不亂的頭發(fā)上寒跳,一...
    開封第一講書人閱讀 49,036評論 1 285
  • 那天,我揣著相機(jī)與錄音竹椒,去河邊找鬼童太。 笑死,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的康愤。 我是一名探鬼主播儡循,決...
    沈念sama閱讀 38,349評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼征冷!你這毒婦竟也來了择膝?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,979評論 0 259
  • 序言:老撾萬榮一對情侶失蹤检激,失蹤者是張志新(化名)和其女友劉穎肴捉,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體叔收,經(jīng)...
    沈念sama閱讀 43,469評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡齿穗,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,938評論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了饺律。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片窃页。...
    茶點故事閱讀 38,059評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖复濒,靈堂內(nèi)的尸體忽然破棺而出脖卖,到底是詐尸還是另有隱情,我是刑警寧澤巧颈,帶...
    沈念sama閱讀 33,703評論 4 323
  • 正文 年R本政府宣布畦木,位于F島的核電站,受9級特大地震影響砸泛,放射性物質(zhì)發(fā)生泄漏十籍。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,257評論 3 307
  • 文/蒙蒙 一唇礁、第九天 我趴在偏房一處隱蔽的房頂上張望勾栗。 院中可真熱鬧,春花似錦垒迂、人聲如沸械姻。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,262評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至绣夺,卻和暖如春吏奸,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背陶耍。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工奋蔚, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 45,501評論 2 354
  • 正文 我出身青樓泊碑,卻偏偏與公主長得像坤按,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子馒过,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,792評論 2 345

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