阿里云iOS客戶端適配Swift 3.0小記

阿里云App從Swift 2.1開(kāi)始使用Swift穗椅,隨時(shí)不斷的推進(jìn),現(xiàn)在所有的業(yè)務(wù)代碼都用Swift編寫(xiě)。由于Swift 3.0語(yǔ)法上有諸多改變册舞,所以從Swift 2.3升級(jí)到Swift 3.0是一件宜早不宜遲的事情。元旦期間抽了點(diǎn)時(shí)間做這個(gè)升級(jí)障般。

外部依賴

  • 目前開(kāi)源社區(qū)對(duì)Swift 3.0支持是非常好的调鲸,我們依賴的開(kāi)源組件最新版本都支持Swift 3.0了,所以并沒(méi)有什么不能解決的依賴挽荡。
  • 因?yàn)楹芏嘟M件依賴CocoaPods 1.x才能編譯藐石,所以前期我們花了一些時(shí)間支持CocoaPods 1.1.1。
  • 因?yàn)槟壳癝wift相關(guān)的動(dòng)態(tài)庫(kù)仍然會(huì)打進(jìn)App里面定拟,所以升級(jí)到Swift 3.0于微,并不影響對(duì)iOS8、iOS9的支持青自。

升級(jí)過(guò)程

  • 先將所有我們自己的組件一個(gè)一個(gè)升級(jí)到Swift 3.0的語(yǔ)法株依。組件化的好處是可以并行,先解決基礎(chǔ)組件延窜,然后大家并行解決高級(jí)組件恋腕。
  • 最后升級(jí)主工程,能解決的解決掉需曾,不能解決的加上//TODO: Swift 3.0 小明這樣的注釋吗坚。保證主工程能運(yùn)行通過(guò)之后,大家并行解決這些TODO問(wèn)題呆万。
  • Xcode自帶的convert還是很給力的商源,因?yàn)閄code不會(huì)一次轉(zhuǎn)到位,可以不斷執(zhí)行convert進(jìn)行轉(zhuǎn)換谋减。不過(guò)convert有點(diǎn)費(fèi)時(shí)牡彻,主工程有將近400個(gè)Swift文件,完整的convert一次需要兩個(gè)小時(shí)左右,所以后面我都是自己根據(jù)已知的規(guī)則去做replace庄吼。Xcode目前只支持對(duì)target做convert缎除,不支持對(duì)文件或者代碼片段做convert带欢,有點(diǎn)蛋疼亩进。
screenshot.png
  • 總耗時(shí)。10萬(wàn)行代碼抵窒,6位同學(xué)元旦期間利用業(yè)余時(shí)間完成渐行。

細(xì)節(jié)

Swift 3.0改變最大的地方如下所示轰坊。

  • 所有枚舉類型首字母改成小寫(xiě)
//Swift 2.3
label.textAlignment = .Right

//Swift 3.0
label.textAlignment = .right
  • 所有顏色去掉了后面的Color()
//Swift 2.3
label.textColor = UIColor.redColor()

//Swift 3.0
label.textColor = UIColor.red
  • 所有的方法第一個(gè)參數(shù)默認(rèn)增加argument label。但是函數(shù)類型不能使用argument label祟印。這導(dǎo)致了很詭異的不一致肴沫,并且參數(shù)多的時(shí)候,體驗(yàn)也不好蕴忆,是一個(gè)奇怪的改變颤芬。詳細(xì)情況請(qǐng)參看:Remove type system significance of function argument labels
//argument label構(gòu)成重載
func foo(x: Int) {}

func foo(foo x: Int) {}

func foo(bar x: Int) {}

foo(x:)(5)
foo(foo:)(5)
foo(bar:)(5)

//可以隨意賦值給參數(shù)類型相同的函數(shù)
var fn1 : (Int) -> Void

fn1 = foo(x:)
fn1 = foo(foo:)
fn1 = foo(bar:)

var Fooblock : ((Int, Int) -> Void) = { (a, b) in
    print("\(a) \(b)")
}

//這樣寫(xiě)也OK
var Fooblock : ((_ a: Int, _ b : Int) -> Void) = { (a, b) in
    print("\(a) \(b)")
}

//用的時(shí)候沒(méi)有任何arguement label
fooBlock(3, 4)
  • 大量方法改名
//Swift 2.3
label.font = UIFont.systemFontOfSize(17)

//Swift 3.0
label.font = UIFont.systemFont(ofSize: 17)

有些OC方法被改得連爹媽都不認(rèn)識(shí)了套鹅,比如OC ALYGetURLParams->Swift alyGetParams站蝠。

  • 少量方法變成了屬性
//Swift 2.3
override func preferredStatusBarStyle() -> UIStatusBarStyle {
    return .LightContent
}

override public func intrinsicContentSize() -> CGSize {

}

//Swift 3.0
override var preferredStatusBarStyle : UIStatusBarStyle {
    return .lightContent
}

override var intrinsicContentSize: CGSize {

}
  • if/guard let,每個(gè)條件都要寫(xiě)自己的let卓鹿,where要換成,沉衣。

  • Optional更加嚴(yán)謹(jǐn)了,Optional和非Optional不能比較减牺,Optional和Optional也不能比較。為了支持原有的比較代碼存谎,Converter會(huì)自動(dòng)插入運(yùn)算符重載的代碼拔疚,比如下面這一大段FIXME代碼。

//Swift 3.0
let foo : Float? = 1.0
let bar : Float? = 2.0
if let _foo = foo,
    let _bar = bar,
    _foo > _bar {

}
// FIXME: comparison operators with optionals were removed from the Swift Standard Libary.
// Consider refactoring the code to use the non-optional operators.
fileprivate func < <T : Comparable>(lhs: T?, rhs: T?) -> Bool {
  switch (lhs, rhs) {
  case let (l?, r?):
    return l < r
  case (nil, _?):
    return true
  default:
    return false
  }
}

// FIXME: comparison operators with optionals were removed from the Swift Standard Libary.
// Consider refactoring the code to use the non-optional operators.
fileprivate func > <T : Comparable>(lhs: T?, rhs: T?) -> Bool {
  switch (lhs, rhs) {
  case let (l?, r?):
    return l > r
  default:
    return rhs < lhs
  }
}
  • 實(shí)現(xiàn)大量的非NS對(duì)象既荚,比如Data稚失、Date、IndexPath恰聘、URL句各、Error等等,這些類型和NS類型是可以相互轉(zhuǎn)型的晴叨,所以改起來(lái)還是很快的凿宾。

  • 大量的unused警告,需要_接一下兼蕊。

_ = self.navigationController?.popViewController(animated: true)

自己定義的接口可以使用@discardableResult消除警告初厚,對(duì)于鏈?zhǔn)綐?gòu)造函數(shù)來(lái)說(shuō)非常有用。

@discardableResult
open class func routeURL(_ url: URL?) -> Bool {
    return JLRoutes.routeURL(url)
}
  • 逃逸的block都要加上@escaping修飾符孙技。@escaping不構(gòu)成重載产禾,但是成為判斷是否實(shí)現(xiàn)協(xié)議接口的依據(jù)排作。
public typealias FooBlock = () -> Void

func FooFunc(_ block: FooBlock) {
}

//@escaping 并不會(huì)構(gòu)成重載,聲明下面兩個(gè)函數(shù)會(huì)報(bào)redeclaration錯(cuò)誤亚情。
//func FooFunc(_ block: @escaping FooBlock) {
//}


protocol FooProtocol {
    func doBlock(block: @escaping FooBlock)
}

//但是@escaping會(huì)影響實(shí)現(xiàn)協(xié)議接口
class FooClass : FooProtocol {

    //OK
    func doBlock(block: @escaping () -> Void) {
        block()
    }

    //會(huì)提示沒(méi)有實(shí)現(xiàn)FooProtocol
//    func doBlock(block: () -> Void) {
//        block()
//    }
}
  • dispatch相關(guān)的方法都改寫(xiě)了妄痪,變得更加簡(jiǎn)潔,更加面向?qū)ο罅恕?/li>
//Swift 2.3
let delayTime = dispatch_time(DISPATCH_TIME_NOW, 0.5)
dispatch_after(delayTime, queue, {
    block()
})

//Swift 3.0
DispatchQueue.main.asyncAfter(deadline: 0.5, execute: {
    block()
})
  • CGPoint楞件、CGRect相關(guān)函數(shù)構(gòu)造都需要加上對(duì)應(yīng)的argument label衫生。
//Swift 3.0
CGPoint(x: 0, y: 0)
CGSize(width: 500, height: 500)
CGRect(x: 0, y: 0, width: 500, height: 500)

CGPoint.zero
CGSize.zero
CGRect.zero
  • 新增openfileprivate等關(guān)鍵字履因。需要被繼承的類障簿、需要被override的方法,都要用open修飾栅迄。extension不能用open修飾站故,但是它的方法只要加了open修飾,也可以在子類里面override毅舆。

使用OC代碼

  • OC的NS_Options類型會(huì)轉(zhuǎn)換成OptionSet西篓,而等于0的那一項(xiàng)作為默認(rèn)值是看不到,非常詭異憋活。默認(rèn)值可以通過(guò)AspectOptions(rawValue: 0)[]得到岂津。
//OC
typedef NS_OPTIONS(NSUInteger, AspectOptions) {
    AspectPositionAfter   = 0,            /// Called after the original implementation (default)
    AspectPositionInstead = 1,            /// Will replace the original implementation.
    AspectPositionBefore  = 2,            /// Called before the original implementation.
    
    AspectOptionAutomaticRemoval = 1 << 3 /// Will remove the hook after the first execution.
};

//Swift
public struct AspectOptions : OptionSet {

    public init(rawValue: UInt)

    
    /// Called after the original implementation (default)
    public static var positionInstead: AspectOptions { get } /// Will replace the original implementation.

    /// Will replace the original implementation.
    public static var positionBefore: AspectOptions { get } /// Called before the original implementation.

    
    /// Called before the original implementation.
    public static var optionAutomaticRemoval: AspectOptions { get } /// Will remove the hook after the first execution.
}
  • 之前Swift類型放到OC容器里面,會(huì)自動(dòng)轉(zhuǎn)型為AnyObject≡眉矗現(xiàn)在需要自己用as轉(zhuǎn)型吮成。Any和AnyObject的區(qū)別可以看到這篇文章:ANY 和 ANYOBJECT

  • 使用OC代碼的時(shí)候辜梳,NSDictionary會(huì)變成[AnyHashable: Any]粱甫,很多時(shí)候還得轉(zhuǎn)回Dictionary/NSDictionary繼續(xù)使用,好在as轉(zhuǎn)型也是OK的作瞄。

typedef void (^LOGIN_COMPLETION_HANDLER) (BOOL isSuccessful, NSDictionary* loginResult);
screenshot.png
  • OC的構(gòu)造函數(shù)如果返回id也會(huì)變成Any類型茶宵,用的時(shí)候需要強(qiáng)轉(zhuǎn)一下,比較惡心宗挥。所以要使用更新的instanceType吧乌庶。
//會(huì)返回Any
+ (id) sharedInstantce;

//用的時(shí)候需要不斷強(qiáng)轉(zhuǎn)
(TBLoginCenter.sharedInstantce() as! TBLoginCenter).login()

//要用下面這種
+ (instancetype) sharedInstance;

  • 對(duì)于protocol中的optional接口,自動(dòng)convert契耿、手動(dòng)處理可能會(huì)出錯(cuò)瞒大,搞錯(cuò)了不會(huì)有警告或者錯(cuò)誤,但是代碼邏輯會(huì)出錯(cuò)宵喂。
screenshot.png
  • ImplicitlyUnwrappedOptional語(yǔ)義變了糠赦,不會(huì)自動(dòng)解包了。以前如果用到IUO的地方需要注意,要不然可能會(huì)出現(xiàn)下面這種情況拙泽。
screenshot.png
screenshot.png

客戶端的問(wèn)題很容易發(fā)現(xiàn)淌山,傳遞給服務(wù)器端的參數(shù)如果也有同樣的問(wèn)題會(huì)很蛋疼,因此可以考慮在網(wǎng)絡(luò)底層檢查一下參數(shù)是否包含Optional顾瞻。如果發(fā)現(xiàn)有泼疑,那么直接abort掉。

  • 類型沖突荷荤。比如自己也實(shí)現(xiàn)了一個(gè)Error對(duì)象退渗,那么會(huì)跟Swift Error沖突,可以用Swift.Error這種方式解決蕴纳。
override open func webView(_ webView: UIView!, didFailLoadWithError error: Swift.Error!) {
    super.webView(webView, didFailLoadWithError: error)
}
  • 有些代碼會(huì)導(dǎo)致Swift編譯器進(jìn)程崩潰会油。比如一個(gè)OC庫(kù)里面有個(gè)這樣的接口,最后的error參數(shù)用Nonnull修飾會(huì)導(dǎo)致Swift編譯器編譯過(guò)程中崩潰古毛。
- (id _Nonnull)initWithFileInfo:(ARUPFileInfo * _Nonnull)fileInfo
                    bizeType:(NSString *_Nonnull)bizType
                    propress:(ProgressBlock _Nullable )progress
                    success:(SuccessBlock _Nullable )success
                    faile:(FailureBlock _Nullable )faile
                    networkSwitch:(NetworkSwitchBlock _Nullable)networkSwitch
                    error:(NSError *_Nonnull*_Nonnull)error;
Snip20170103_1.png

蘋(píng)果官方bug系統(tǒng)上也有人提過(guò)這個(gè)問(wèn)題翻翩,參考:https://bugs.swift.org/browse/SR-3272。去掉Nonnull修飾即可通過(guò)編譯稻薇。

編譯顯著變慢

升級(jí)Swift 3.0之后嫂冻,感覺(jué)編譯賊慢。根據(jù):Profiling your Swift compilation times這篇文章的方法加上-Xfrontend -debug-time-function-bodies之后塞椎,發(fā)現(xiàn)排名前五的方法都是50s左右桨仿。總的編譯時(shí)間比2.3要慢一倍案狠。2.3和3.0版本編譯都很慢服傍,但是3.0要更慢。

Swift 2.3: 342,403ms
Swift 3.0: 579,519ms
screenshot.png
screenshot.png

我頓時(shí)感到我大好青春都浪費(fèi)在編譯上面了骂铁,所以我們趕快來(lái)看看這段代碼寫(xiě)了什么東西伴嗡。

override func fetchDataForSinglePageTableView(_ actionType: ALYLoadDataActionType, successCallback: @escaping GetPageDataSuccessCallback, failedCallback: @escaping GetPageDataFailedCallback) {

    //blablabla

    successCallback(UInt((self?.statusInfo.count ?? 0) + (self?.regularInfo.count ?? 0) + (self?.nameServerInfo.count ?? 0)))

}) { [weak self] (exception) -> Void in
        failedCallback(exception)
        self?.refreshButton.isHidden = false
        self?.showFailureToast(exception.reason)
    }
}

這么大一段函數(shù),初看沒(méi)有明確的目標(biāo)从铲,于是我查找了資料,看看是否有前人的經(jīng)驗(yàn)可以借鑒澄暮,結(jié)果果然有很多人遇到相同的問(wèn)題名段,現(xiàn)有的總結(jié)已經(jīng)很詳細(xì)我不再贅述,這里主要參考了:Swift 工程速度編譯慢泣懊。對(duì)比這篇總結(jié)伸辟,我猜測(cè)應(yīng)該是下面這行將幾個(gè)連續(xù)的??相加導(dǎo)致的。

//老代碼
successCallback(UInt((self?.statusInfo.count ?? 0) + (self?.regularInfo.count ?? 0) + (self?.nameServerInfo.count ?? 0)))

//新代碼
let statusCnt = self?.statusInfo.count ?? 0
let regularCnt = self?.regularInfo.count ?? 0
let nameServerCnt = self?.nameServerInfo.count ?? 0
successCallback(UInt(statusCnt + regularCnt + nameServerCnt))

再跑一下測(cè)試命令馍刮,編譯時(shí)間馬上變成78ms信夫,差了將近1000倍!

78.3ms  /Users/chantu/test3/cloudconsole-iOS/CloudConsoleApp/Domain/Domain+Register/ALYDomainRegisterMsgViewController.swift:102:19 @objc override func fetchDataForSinglePa      geTableView(_ actionType: ALYLoadDataActionType, successCallback: @escaping GetPageDataSuccessCallback, failedCallback: @escaping GetPageDataFailedCallback)

基于這個(gè)思路,我主要修改了以下兩種情況的代碼静稻。

  • 幾個(gè)??同時(shí)出現(xiàn)在一個(gè)表達(dá)式里面的警没,如上述代碼
  • ??出現(xiàn)在字典里面的,如下面這種振湾。
var param:[String: String] = [
    "securityGroupId": self.belongGroupId ?? "",
    "regionId": self.regionId ?? "",
    "ipProtocol": self.protocolType ?? "",
    "portRange": self.portRange ?? "",
    "policy": self.policy ?? "",
    "priority": self.priority ?? "",
    "nicType": self.nicType ?? ""
]

為了保持寫(xiě)代碼的流暢性杀迹,不因?yàn)榫幾g問(wèn)題影響大家編碼的體驗(yàn),因此我只對(duì)幾個(gè)特別耗時(shí)的地方做了修改押搪,但是可以從測(cè)試結(jié)果看到树酪,編譯速度有了明顯的提升,下面是測(cè)試后跑出來(lái)的時(shí)間大州⌒铮可以看到最慢的也只有1s多了,比之前的47s好太多了厦画。

1289.2ms    /Users/chantu/test3/cloudconsole-iOS/CloudConsoleApp/Domain/DomainRealNameVerify/ALYDomainRealNameVerifyUploadInfoDataService.swift:117:10  func uploadInfo(_ templateId: String, credentialsNo: String, credentialsType: ALYDomainRealNameVerifyCredentialsType, credentialsImageData: Data, completionBlock: @escaping ((_ isSuccess: Bool, _ errorMsg: String) -> Void))
1084.8ms    /Users/chantu/test3/cloudconsole-iOS/CloudConsoleApp/Instance/ECS/Disk/ALYECSDiskDetailViewController.swift:242:10  func setcellContentsWithModel(_ model: ALYECSDiskDetailModel)
1038.6ms    /Users/chantu/test3/cloudconsole-iOS/CloudConsoleApp/Instance/ECS/Disk/ALYECSDiskDetailViewController.swift:242:10  func setcellContentsWithModel(_ model: ALYECSDiskDetailModel)
1027.7ms    /Users/chantu/test3/cloudconsole-iOS/CloudConsoleApp/Instance/Data/ALYCloudMetricDataService.swift:15:10    func getInstanceMetric(withPluginId pluginId: String, dimensions: String, metric: String, startTime: Double, endTime: Double, successCallback: @escaping ALYServiceSuccessCallback, failureCallback: @escaping ALYServiceFailureCallback)
999.3ms /Users/chantu/test3/cloudconsole-iOS/CloudConsoleApp/Domain/DomainRealNameVerify/ALYDomainRealNameVerifyUploadInfoDataService.swift:117:10  func uploadInfo(_ templateId: String, credentialsNo: String, credentialsType: ALYDomainRealNameVerifyCredentialsType, credentialsImageData: Data, completionBlock: @escaping ((_ isSuccess: Bool, _ errorMsg: String) -> Void))

我們目前的做法是盡量不把這些復(fù)雜的操作寫(xiě)到一個(gè)表達(dá)式里面疮茄,先把變量存起來(lái)再放到表達(dá)式里計(jì)算,雖然是因?yàn)檎Z(yǔ)言的問(wèn)題不得不妥協(xié)但為了自己編譯速度還是寧可多寫(xiě)幾行苛白。

參考資料

  1. Migrating to Swift 2.3 or Swift 3 from Swift 2.2
  2. Swift3.0變化

總結(jié)

因?yàn)镾wift 3.0版本開(kāi)始保證接口的穩(wěn)定性娃豹,這次升級(jí)到3.0之后,使用Swift再無(wú)后顧之憂购裙。希望蘋(píng)果真的不要再干出Swift 1.0->2.0->3.0每次升級(jí)都要改大量代碼的扯淡事懂版。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市躏率,隨后出現(xiàn)的幾起案子躯畴,更是在濱河造成了極大的恐慌,老刑警劉巖薇芝,帶你破解...
    沈念sama閱讀 221,430評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件蓬抄,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡夯到,警方通過(guò)查閱死者的電腦和手機(jī)嚷缭,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,406評(píng)論 3 398
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)耍贾,“玉大人阅爽,你說(shuō)我怎么就攤上這事〖隹” “怎么了付翁?”我有些...
    開(kāi)封第一講書(shū)人閱讀 167,834評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)晃听。 經(jīng)常有香客問(wèn)我百侧,道長(zhǎng)砰识,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 59,543評(píng)論 1 296
  • 正文 為了忘掉前任佣渴,我火速辦了婚禮辫狼,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘观话。我一直安慰自己予借,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,547評(píng)論 6 397
  • 文/花漫 我一把揭開(kāi)白布频蛔。 她就那樣靜靜地躺著灵迫,像睡著了一般。 火紅的嫁衣襯著肌膚如雪晦溪。 梳的紋絲不亂的頭發(fā)上瀑粥,一...
    開(kāi)封第一講書(shū)人閱讀 52,196評(píng)論 1 308
  • 那天,我揣著相機(jī)與錄音三圆,去河邊找鬼狞换。 笑死,一個(gè)胖子當(dāng)著我的面吹牛舟肉,可吹牛的內(nèi)容都是我干的修噪。 我是一名探鬼主播,決...
    沈念sama閱讀 40,776評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼路媚,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼黄琼!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起整慎,我...
    開(kāi)封第一講書(shū)人閱讀 39,671評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤脏款,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后裤园,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體撤师,經(jīng)...
    沈念sama閱讀 46,221評(píng)論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,303評(píng)論 3 340
  • 正文 我和宋清朗相戀三年拧揽,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了剃盾。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,444評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡淤袜,死狀恐怖万俗,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情饮怯,我是刑警寧澤,帶...
    沈念sama閱讀 36,134評(píng)論 5 350
  • 正文 年R本政府宣布嚎研,位于F島的核電站蓖墅,受9級(jí)特大地震影響库倘,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜论矾,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,810評(píng)論 3 333
  • 文/蒙蒙 一教翩、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧贪壳,春花似錦饱亿、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,285評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至蚂且,卻和暖如春配猫,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背杏死。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,399評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工泵肄, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人淑翼。 一個(gè)月前我還...
    沈念sama閱讀 48,837評(píng)論 3 376
  • 正文 我出身青樓腐巢,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親玄括。 傳聞我的和親對(duì)象是個(gè)殘疾皇子冯丙,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,455評(píng)論 2 359

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

  • 發(fā)現(xiàn) 關(guān)注 消息 iOS 第三方庫(kù)、插件惠豺、知名博客總結(jié) 作者大灰狼的小綿羊哥哥關(guān)注 2017.06.26 09:4...
    肇東周閱讀 12,118評(píng)論 4 61
  • 我總相信银还, 日有所思,夜有所夢(mèng) 倘有一次洁墙, 在那虛幻而真實(shí)的夢(mèng)里見(jiàn)到蛹疯, 也會(huì)感到欣喜。 仿佛自己的朝思暮想有了回應(yīng)热监。
    雲(yún)淡風(fēng)輕cloudy閱讀 121評(píng)論 1 1
  • 最新戰(zhàn)報(bào):進(jìn)入TOP 10最終角逐的文章已經(jīng)出來(lái)啦孝扛,這些文章將遞交至次大賽評(píng)委處參加最終角逐列吼!具體可戳「青春映象節(jié)...
    我是簡(jiǎn)小妹閱讀 1,222評(píng)論 33 16
  • 從前有一只流浪的白色小狗,她在各個(gè)地方漂泊苦始∧浚渴了就去跑到小河邊喝水,順便梳洗臟兮兮的自己陌选,餓了就在街上各個(gè)攤點(diǎn)尋找...
    睡午覺(jué)的夏暖暖閱讀 703評(píng)論 0 1