用 RxSwift 為 Controller 瘦身(1)奠货,優(yōu)雅的使用網(wǎng)絡(luò)請(qǐng)求(Moya) + 數(shù)據(jù)緩存(Cache)介褥。

概述

View Controller 向來(lái)是 MVC (Model-View-View Controller) 中最讓人頭疼的一環(huán),MVC 架構(gòu)本身并不復(fù)雜递惋,但開(kāi)發(fā)者很容易將大量代碼扔到用于協(xié)調(diào) View 和 Model 的 Controller 中柔滔。你不能說(shuō)這是一種錯(cuò)誤,因?yàn)?View Controller 所承擔(dān)的本來(lái)就是膠水代碼和業(yè)務(wù)邏輯的部分丹墨。但是廊遍,持續(xù)這樣做必定將導(dǎo)致 Model View Controller 變成 Massive View Controller,代碼也就一天天爛下去贩挣,直到?jīng)]人敢碰喉前。

寫(xiě)到后來(lái),幾經(jīng)變換王财,最后你的 Controller 常常就變成了這樣


Controller 中含有大量代碼的一個(gè)很大原因在于卵迂,大多數(shù)人都誤用了 MVC,推薦可以看看喵神的這兩篇文章绒净,深入淺出见咒。
關(guān)于 MVC 的一個(gè)常見(jiàn)的誤用
單向數(shù)據(jù)流動(dòng)的函數(shù)式 View Controller

這篇文章我們先從網(wǎng)絡(luò)層入手,在 iOS 開(kāi)發(fā)中挂疆,網(wǎng)絡(luò)請(qǐng)求與數(shù)據(jù)解析可以說(shuō)是其中占比很高并且不可分割的一部分改览。

身為一名 iOS 開(kāi)發(fā),也許你不知道 NSUrlConnection缤言、也不知道 NSURLSession宝当,但你一定知道 AFNetworking / Alamofire。對(duì)他們你肯定也做過(guò)一些自己的封裝胆萧,或者直接采用業(yè)內(nèi)比較知名的第三方封裝庆揩。比如 Objective-C 中的 YTKNetworkSwift 中的 Moya 等等。

那么問(wèn)題來(lái)了订晌,無(wú)論是自己封裝也好還是直接采用第三方也好虏辫,在我們熟知的 MVC 模式中,你依舊需要在 Controller 中回調(diào) Block / Delegate 對(duì)其做出處理锈拨,比如對(duì)返回?cái)?shù)據(jù)的校驗(yàn)與解析砌庄,對(duì)指示器的控制,對(duì)刷新控件的控制推励,把 Model 賦值給 View 等等鹤耍。而且在 iOS 中 Controller 本身就包含了一個(gè) View,對(duì)其生命周期的管理和界面布局無(wú)疑又增加了 Controller 的負(fù)擔(dān)验辞。

久而久之稿黄,當(dāng)控制器中再加入一些其他的業(yè)務(wù)邏輯時(shí),整個(gè)控制器里的代碼就會(huì)變得非常臃腫跌造,巨胖無(wú)比杆怕,隨著業(yè)務(wù)的變更,代碼的可讀性會(huì)變得很差壳贪。其實(shí) Controller 中大多數(shù)代碼都可以被抽離出去陵珍,比如說(shuō)我們的網(wǎng)絡(luò)請(qǐng)求。

讓網(wǎng)絡(luò)請(qǐng)求的代碼更優(yōu)雅

本篇文章我們主要是針對(duì) Moya 的再次封裝擴(kuò)展违施。其實(shí) Moya 本身對(duì)網(wǎng)絡(luò)層的封裝已經(jīng)很優(yōu)秀了互纯,自帶了對(duì)于 RxSwift 這類函數(shù)響應(yīng)式庫(kù)的擴(kuò)展,網(wǎng)絡(luò)層非常清晰磕蒲,并且提供了簡(jiǎn)單方便的網(wǎng)絡(luò)單元測(cè)試留潦。但我們依然可以把她變得更好。

封裝 Moya

Moya 的使用我在這里就不貼了辣往,沒(méi)用過(guò)的小伙伴可以去官方文檔學(xué)習(xí)一下兔院。

用過(guò)的小伙伴知道,我們使用 Moya 都要先創(chuàng)建一個(gè) Enum 遵守 TargetType 協(xié)議實(shí)現(xiàn)對(duì)應(yīng)的方法(比如指定請(qǐng)求的 URL 路徑站削,參數(shù)等等)坊萝。

public enum GitHub {
    case userProfile(String)
}
extension GitHub: TargetType {

    public var baseURL: URL { return URL(string: "https://api.github.com")! }

    public var path: String {
        switch self {
        case .userProfile(let name):
            return "/users/\(name.urlEscaped)"
        }
    }

    public var method: Moya.Method {
        return .get
    }

    public var task: Task {
        switch self {
        default:
            return .requestPlain
        }
    }
}

而實(shí)際的請(qǐng)求是使用 MoyaProvider<Target> 類,傳入一個(gè)遵守 TargetType 協(xié)議的 Enum许起,創(chuàng)建 MoyaProvider 對(duì)象去請(qǐng)求的十偶。

provider = MoyaProvider<GitHub>()
provider.request(.userProfile("InsectQY")) { result in
    // do something with the result
}

可是如果把項(xiàng)目中所有的網(wǎng)絡(luò)請(qǐng)求都寫(xiě)在同一個(gè) Enum 中的話,這個(gè)Enum里的代碼會(huì)非常多园细,維護(hù)起來(lái)也并不方便扯键。

筆者在使用時(shí)通常都是根據(jù)模塊創(chuàng)建多個(gè) Enum,比如按首頁(yè)模塊珊肃,新聞模塊這樣劃分。如果這么寫(xiě)的話,我們創(chuàng)建 MoyaProvider 對(duì)象時(shí)就不能再傳入指定類型的 Enum 了伦乔。我們把創(chuàng)建對(duì)象的寫(xiě)法改成 MoyaProvider<MultiTarget>厉亏,所有傳入的 Enum 得用 MultiTarget 包裝一層。

let provider = MoyaProvider<MultiTarget>
provider.request(MultiTarget(GitHub.userProfile("InsectQY"))) { result in
    // do something with the result
}

看了上面的代碼烈和,好像已經(jīng)開(kāi)始變得不那么優(yōu)雅了爱只,我指定一個(gè)請(qǐng)求竟然要寫(xiě)這么多代碼,一大堆括號(hào)看的眼睛都暈招刹。能不能直接使用 Enum 的類型不需要借助 MoyaProvider 對(duì)象去請(qǐng)求呢恬试,類似這樣的效果。

GitHub.userProfile("InsectQY").request

以下封裝我們基于 RxSwift 來(lái)實(shí)現(xiàn)疯暑,當(dāng)然如果你不熟悉 RxSwift 也沒(méi)關(guān)系训柴,這里只是對(duì)封裝思路的介紹,封裝完成以后可以直接使用妇拯,等以后熟悉了 RxSwift 再回頭看也行幻馁。以下文章的思路大多借鑒 RxNetwork 這個(gè)庫(kù)的實(shí)現(xiàn)。

首先我們?yōu)?TargetType 添加自己的 public extension 方便外界調(diào)用越锈。

public extension TargetType {

}

先實(shí)現(xiàn)一個(gè)可以直接使用 Enum 類型調(diào)用請(qǐng)求的方法仗嗦。

let provider = MoyaProvider<MultiTarget>

public extension TargetType {
   func request() -> Single<Response> {
       return provider.rx.request(.target(self))
   }
}

這個(gè)方法返回一個(gè) Single 類型的 ObservableSingleObservable 的另一個(gè)版本甘凭。它不像 Observable 可以發(fā)出多個(gè)元素稀拐,它要么只能發(fā)出一個(gè)元素,要么產(chǎn)生一個(gè) error 事件丹弱,不共享狀態(tài)變化德撬,用來(lái)做請(qǐng)求的返回非常合適。

寫(xiě)完我們就可以直接用 Enum 調(diào)用請(qǐng)求蹈矮,怎么樣是不是非常簡(jiǎn)單呢砰逻。代碼的可讀性也變高了很多。對(duì)請(qǐng)求的結(jié)果只需要調(diào)用 subscribe 去監(jiān)聽(tīng)即可泛鸟。

GitHub.userProfile("InsectQY").request.subscribe...

封裝 JSON 解析

先回顧一下我們以往的 JSON 解析蝠咆,通常都是使用第三方解析庫(kù),直接把代碼放到每次請(qǐng)求的回調(diào)中去處理北滥。

乍一看其實(shí)沒(méi)毛病刚操,那么這么做有什么弊端呢?其實(shí)這種寫(xiě)法侵入性很強(qiáng)再芋,試想一下假如有一天你這個(gè)第三方解析庫(kù)不維護(hù)了菊霜,或者種種原因你需要更換到其他的第三方,或者自己手寫(xiě)解析济赎,那么你需要替換和修改的地方就非常多鉴逞。

你可能會(huì)說(shuō)记某,那我可以在第三方解析的方法上封裝一層,然后調(diào)用我自己的解析方法啊构捡。是的液南,想法很好,但你有沒(méi)有想過(guò)其實(shí)解析的寫(xiě)法可以變得非常優(yōu)雅勾徽。

Moya 自身就提供了基于 Codable 協(xié)議的原生解析方法滑凉。

public func map<D>(_ type: D.Type, atKeyPath keyPath: String? = default, using decoder: JSONDecoder = default, failsOnEmptyData: Bool = default) throws -> D where D : Decodable

支持對(duì) JSON 指定路徑的解析,實(shí)現(xiàn)的原理也非常簡(jiǎn)單喘帚,感興趣的小伙伴可以去源碼中學(xué)習(xí)一下畅姊。具體位置在 Response 這個(gè)類中搜索關(guān)鍵詞即可。
這個(gè)方法我們直接就能使用吹由,轉(zhuǎn)模型的代碼可以寫(xiě)成這樣

GitHub.userProfile("InsectQY").request
.map(UserModel.self)

當(dāng)然最好我們還是在原生方法上再封裝一層若未,減少原生方法對(duì)項(xiàng)目的侵入性。

需要注意的是溉知,在我們平時(shí)使用 Codable 協(xié)議時(shí)陨瘩,通常都要分清解析的是數(shù)組還是字典。如果是數(shù)組類型數(shù)據(jù)的話级乍,必須得調(diào)用指定解析數(shù)組的方法舌劳,否則無(wú)法正確解析。

Moya 是可以在外界直接傳入數(shù)組類型的玫荣,具體實(shí)現(xiàn)也非常簡(jiǎn)單甚淡。用一個(gè) Struct 的結(jié)構(gòu)體去包裝每次需要解析的對(duì)象,再把解析對(duì)象指定為包裝好的結(jié)構(gòu)體捅厂。

private struct DecodableWrapper: Decodable {
    let value: T
}

這樣就不用關(guān)心外界需要解析的具體類型贯卦,相當(dāng)于每次解析的必然是一個(gè)包裝好的字典類型,最后只要把結(jié)構(gòu)體里的 value 返回就行焙贷。

扯一個(gè)題外話撵割,那這種實(shí)現(xiàn)思路在 Objective-C 中是否可行呢,可以思考如下兩個(gè)問(wèn)題辙芍。

  1. Objective-C 中我們使用 MJExtension / YYModel 這些庫(kù)去解析 JSON 時(shí)啡彬,都要調(diào)用指定的解析方法(數(shù)組和字典的解析方法是不同的),能否用以上的思路把解析數(shù)組和解析字典的方法整合成一個(gè)方法呢故硅?
  2. 如果要解析的模型中有個(gè)數(shù)組屬性庶灿,數(shù)組里面又要裝著其他模型。還要寫(xiě)指定數(shù)組內(nèi)部類型的方法吃衅。
// Tell MJExtension what type of model will be contained in statuses and ads.
[StatusResult mj_setupObjectClassInArray:^NSDictionary *{
    return @{
               @"statuses" : @"Status",
               // @"statuses" : [Status class],
               @"ads" : @"Ad"
               // @"ads" : [Ad class]
           };
}];
+ (NSDictionary *)modelContainerPropertyGenericClass {
    // value should be Class or Class name.
    return @{@"shadows" : [Shadow class],
             @"borders" : Border.class,
             @"attachments" : @"Attachment" };
}

這么寫(xiě)目的是為了在運(yùn)行時(shí)拿到數(shù)組中元素的具體類型往踢,再用 Runtime 去類中獲取屬性以及 KVC 賦值。如果用泛型指定數(shù)組里元素的具體類型的話徘层,這些方法是否可以省略呢峻呕?

然而很遺憾利职,原生的 Objective-C 是無(wú)法實(shí)現(xiàn)以上想法的。原因在于 Objective-C 的泛型只能算是"偽"泛型山上,僅僅是一個(gè)編譯器特性眼耀,只能在編譯時(shí)為 Xcode 提供具體類型,在運(yùn)行時(shí)是沒(méi)有的佩憾。

封裝網(wǎng)絡(luò)緩存

為了提升用戶體驗(yàn),在實(shí)際開(kāi)發(fā)中干花,有一些內(nèi)容可能會(huì)加載很慢妄帘,我們想先顯示上次的內(nèi)容,等加載成功后池凄,再用最新的內(nèi)容替換上次的內(nèi)容抡驼。也有時(shí)候,由于網(wǎng)絡(luò)處于斷開(kāi)狀態(tài)肿仑,為了更加友好致盟,我們想顯示上次緩存中的內(nèi)容。

網(wǎng)絡(luò)緩存我們基于 Cache 來(lái)實(shí)現(xiàn)锌妻。首先創(chuàng)建一個(gè) CacheManager 統(tǒng)一處理所有的讀取和存儲(chǔ)操作貌矿。我們把讀取模型數(shù)據(jù)和讀取網(wǎng)絡(luò)請(qǐng)求返回的 Response 數(shù)據(jù)分別創(chuàng)建不同的方法(這里只貼了模型的方法)冀值。

// MARK: - 讀取模型緩存
    static func object<T: Codable>(ofType type: T.Type, forKey key: String) -> T? {
        do {
            
            let storage = try Storage(diskConfig: DiskConfig(name: "NetObjectCache"), memoryConfig: MemoryConfig(), transformer: TransformerFactory.forCodable(ofType: type))
            try storage.removeExpiredObjects()
            return (try storage.object(forKey: key))
        } catch {
            return nil
        }
    }
    
    // MARK: - 緩存模型
    static func setObject<T: Codable>(_ object: T, forKey: String) {
        
        do {
            
            let storage = try Storage(diskConfig: DiskConfig(name: "NetCache"), memoryConfig: MemoryConfig(), transformer: TransformerFactory.forCodable(ofType: T.self))
            try storage.setObject(object, forKey: forKey)
        } catch  {
            print("error\(error)")
        }
    }

緩存的方法封裝好以后,我們還需要知道緩存的 key杯道,這里我們采用請(qǐng)求的 URL + 參數(shù)拼接成 key

extension Task {
    
    public var parameters: String {
        switch self {
        case .requestParameters(let parameters, _):
            return "\(parameters)"
        case .requestCompositeData(_, let urlParameters):
            return "\(urlParameters)"
        case let .requestCompositeParameters(bodyParameters, _, urlParameters):
            return "\(bodyParameters)\(urlParameters)"
        default:
            return ""
        }
    }
}
public extension TargetType {
    var cachedKey: String {
        return "\(URL(target: self).absoluteString)?\(task.parameters)"
     }
}

萬(wàn)事俱備责蝠,現(xiàn)在為 TargetType 添加一個(gè) cache 屬性党巾,返回一個(gè) Observable 包裝遵守 TargetType 協(xié)議的 Enum

var cache: Observable<Self> {
    return Observable.just(self)
 }

那么我們調(diào)用緩存的代碼就變成了這樣

GitHub.userProfile("InsectQY").cache

但是這個(gè)緩存還沒(méi)有具體的實(shí)現(xiàn)霜医,現(xiàn)在我們?yōu)榫彺嫣砑訉?shí)現(xiàn)齿拂,只有遵守 TargetType 協(xié)議才能調(diào)用。

每次調(diào)用方法都把請(qǐng)求結(jié)果緩存到本地肴敛,返回?cái)?shù)據(jù)時(shí)先從本地獲取署海,本地沒(méi)有值時(shí)只返回網(wǎng)絡(luò)數(shù)據(jù)。這里的 startWith 保證本地?cái)?shù)據(jù)有值時(shí)值朋,本地?cái)?shù)據(jù)每次都優(yōu)先在網(wǎng)絡(luò)數(shù)據(jù)之前返回叹侄。

extension ObservableType where E: TargetType {
    
    public func request() -> Observable<Response> {
        
        return flatMap { target -> Observable<Response> in
            
            let source = target.request().storeCachedResponse(for: target).asObservable()
            if let response = target.cachedResponse {
                return source.startWith(response)
            }
            return source
        }
    }
}

現(xiàn)在我們的緩存已經(jīng)初步完成了,在 onNext 回調(diào)中昨登,第一次返回的是本地?cái)?shù)據(jù)趾代,第二次是網(wǎng)絡(luò)數(shù)據(jù)。我們的請(qǐng)求就變成了這樣

GitHub.userProfile("InsectQY")
.cache
.request()
.map(UserModel.self)
.subscribe ...

這樣的好處是丰辣,每個(gè)方法之間都是獨(dú)立的撒强,我不想要緩存我只要去掉 cache 不想轉(zhuǎn)模型只要去掉 map 禽捆,整段代碼的可讀性變得很強(qiáng)。

由于 RxSwift 的存在飘哨,你也不需要在 Controller 銷毀時(shí)去手動(dòng)管理網(wǎng)絡(luò)請(qǐng)求的取消胚想。你想做一些網(wǎng)絡(luò)的其他高級(jí)操作也變得非常容易,比如說(shuō)鏈?zhǔn)降木W(wǎng)絡(luò)請(qǐng)求芽隆,group 式的網(wǎng)絡(luò)請(qǐng)求浊服,請(qǐng)求失敗自動(dòng)重試,同一個(gè)請(qǐng)求多次請(qǐng)求時(shí)短時(shí)間忽略相同的請(qǐng)求等等都非常簡(jiǎn)單胚吁。

現(xiàn)在回頭看看我們的需求牙躺,優(yōu)先展示本地?cái)?shù)據(jù),網(wǎng)絡(luò)數(shù)據(jù)返回時(shí)自動(dòng)替換本地?cái)?shù)據(jù)腕扶,網(wǎng)絡(luò)請(qǐng)求失敗時(shí)加載本地?cái)?shù)據(jù)孽拷。

但是這種寫(xiě)法應(yīng)用場(chǎng)景相對(duì)比較單一,只能適用于本地?cái)?shù)據(jù)和網(wǎng)絡(luò)數(shù)據(jù)的處理是相同的情況半抱。我們?cè)?onNext 中無(wú)法區(qū)分本地?cái)?shù)據(jù)和網(wǎng)絡(luò)數(shù)據(jù)脓恕,假如想對(duì)本地?cái)?shù)據(jù)做一些特殊處理的話是不行的。
我們?cè)偻晟埔幌麓a窿侈,將本地?cái)?shù)據(jù)的回調(diào)告訴外界炼幔。

func onCache<T: Codable>(_ type: T.Type, atKeyPath keyPath: String? = "", _ onCache: ((T) -> ())?) -> OnCache<Self, T> {
        
    if let object = cachedObject(type) {onCache?(object)}
    return OnCache(self)
}

返回的 OnCache 對(duì)象是自定義的一個(gè)結(jié)構(gòu)體

public struct OnCache<Target: TargetType, T: Codable> {
    
    public let target: Target
    public let keyPath: String
    
    init(_ target: Target, _ keyPath: String) {
        
        self.target = target
        self.keyPath = keyPath
    }
    
    public func request() -> Single<T> {
        
        return target.request()
                .mapObject(T.self, atKeyPath: keyPath)
                .storeCachedObject(for: target)
    }
}

現(xiàn)在我們就可以在 onCache 的回調(diào)中拿到本地?cái)?shù)據(jù)了,如果你想對(duì)本地?cái)?shù)據(jù)做一些自己的操作和處理的話棉磨,選擇第二種方案會(huì)更加合適江掩。后續(xù)的 subscribe 監(jiān)聽(tīng)到的是一個(gè) Single ,如之前所說(shuō)乘瓤,只會(huì)返回成功或者失敗环形,這里我們只把網(wǎng)絡(luò)數(shù)據(jù)返回就好。這樣就做到了網(wǎng)絡(luò)數(shù)據(jù)和本地?cái)?shù)據(jù)的區(qū)分衙傀。

GitHub.userProfile("InsectQY")
.onCache(UserModel.self, { (local) in
                
})
.request()
.subscribe ...

總結(jié)

好了看了以上這么多抬吟,我們只是對(duì)網(wǎng)絡(luò)層做了一些封裝,還沒(méi)有做這種寫(xiě)法實(shí)際在項(xiàng)目中的應(yīng)用统抬,后續(xù)將教大家如何用 RxSwift 減少控制器的代碼火本。

具體的 demo 和用法可以查看我開(kāi)源的這個(gè)項(xiàng)目 GamerSky
或者原作者的 RxNetwork

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末聪建,一起剝皮案震驚了整個(gè)濱河市钙畔,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌金麸,老刑警劉巖擎析,帶你破解...
    沈念sama閱讀 206,723評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異挥下,居然都是意外死亡揍魂,警方通過(guò)查閱死者的電腦和手機(jī)桨醋,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,485評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)现斋,“玉大人喜最,你說(shuō)我怎么就攤上這事∽#” “怎么了瞬内?”我有些...
    開(kāi)封第一講書(shū)人閱讀 152,998評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)限书。 經(jīng)常有香客問(wèn)我遂鹊,道長(zhǎng),這世上最難降的妖魔是什么蔗包? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,323評(píng)論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮慧邮,結(jié)果婚禮上调限,老公的妹妹穿的比我還像新娘。我一直安慰自己误澳,他們只是感情好耻矮,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,355評(píng)論 5 374
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著忆谓,像睡著了一般裆装。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上倡缠,一...
    開(kāi)封第一講書(shū)人閱讀 49,079評(píng)論 1 285
  • 那天哨免,我揣著相機(jī)與錄音,去河邊找鬼昙沦。 笑死琢唾,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的盾饮。 我是一名探鬼主播采桃,決...
    沈念sama閱讀 38,389評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼丘损!你這毒婦竟也來(lái)了普办?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 37,019評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤徘钥,失蹤者是張志新(化名)和其女友劉穎衔蹲,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體吏饿,經(jīng)...
    沈念sama閱讀 43,519評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡踪危,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,971評(píng)論 2 325
  • 正文 我和宋清朗相戀三年蔬浙,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片贞远。...
    茶點(diǎn)故事閱讀 38,100評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡畴博,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出蓝仲,到底是詐尸還是另有隱情俱病,我是刑警寧澤,帶...
    沈念sama閱讀 33,738評(píng)論 4 324
  • 正文 年R本政府宣布袱结,位于F島的核電站亮隙,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏垢夹。R本人自食惡果不足惜溢吻,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,293評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望果元。 院中可真熱鬧促王,春花似錦、人聲如沸而晒。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,289評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)倡怎。三九已至迅耘,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間监署,已是汗流浹背颤专。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,517評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留焦匈,地道東北人血公。 一個(gè)月前我還...
    沈念sama閱讀 45,547評(píng)論 2 354
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像缓熟,于是被迫代替她去往敵國(guó)和親累魔。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,834評(píng)論 2 345

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

  • 1够滑、通過(guò)CocoaPods安裝項(xiàng)目名稱項(xiàng)目信息 AFNetworking網(wǎng)絡(luò)請(qǐng)求組件 FMDB本地?cái)?shù)據(jù)庫(kù)組件 SD...
    陽(yáng)明先生_x閱讀 15,968評(píng)論 3 119
  • 我像一個(gè)小孩行走在黑暗的森林里彰触,夜晚梯投,我總是能看見(jiàn)星星,但每每想到在這諾大的森林里,只有我一個(gè)人看星星分蓖,就十分無(wú)助...
    杜綾閱讀 638評(píng)論 6 5
  • 馬儷娜閱讀 235評(píng)論 0 0
  • 我們的貴人和伯樂(lè)是誰(shuí)? 他們長(zhǎng)什么樣子蒸甜,在什么地方棠耕? 以便我們能夠更好地辨認(rèn)出他們。 01 我們真正的自己是怎樣的...
    馭爺閱讀 1,227評(píng)論 1 4
  • 供養(yǎng)在鏡框里的天使 在離我很遠(yuǎn)很遠(yuǎn)的地方 擠不進(jìn)她的身邊 無(wú)法穿越過(guò)這透明的高墻 心兒是被她偷襲了的宮殿 她一侵占...
    吳生善閱讀 221評(píng)論 0 1