要不要來點(diǎn) Swift

作者:Jacob Bandes-Storch免绿,原文鏈接俺榆,原文日期:2015-11-28
譯者:mmoaay味悄;校對(duì):千葉知風(fēng)刻获;定稿:shanks

做程序員有一點(diǎn)優(yōu)勢:如果工具不好用郎仆,你自己就可以對(duì)它進(jìn)行優(yōu)化只祠。而 Swift 讓這一點(diǎn)變得尤其簡單,它包含的幾個(gè)特性可以讓你以一種自然的方式對(duì)這門語言進(jìn)行擴(kuò)展和自定義扰肌。

在本文中抛寝,我將分享 Swift 給我編程體驗(yàn)帶來提升的幾個(gè)例子。我希望在讀了本文之后曙旭,你可以認(rèn)識(shí)到使用這門語言時(shí)你自己的痛點(diǎn)在哪盗舰,并付諸實(shí)踐。(當(dāng)然需要先思考9瘐铩)

存在爭議的重復(fù)標(biāo)識(shí)符

下面是你在 Objective-C 中很熟悉的一種情況:枚舉值和字符串常量會(huì)有很長的描述詳細(xì)的名字:

label.textAlignment = NSTextAlignmentCenter;

(這讓我想起了中學(xué)科學(xué)課的格言:在作答時(shí)重復(fù)一下問題钻趋,或者 RQIA,文字是怎么對(duì)齊的剂习?文字是居中對(duì)齊的蛮位。 這在作答方式在超出上下文環(huán)境的時(shí)候很有用,但是其他情況下就顯得比較冗余了进倍。)

Swift 減少了這種冗余土至,因?yàn)槊杜e值可以通過類型名+點(diǎn)符號(hào)來訪問,而且如果你省略了類型名猾昆,它仍然可以被自動(dòng)推斷出來:

label.textAlignment = NSTextAlignment.Center

// 更簡明的:
label.textAlignment = .Center

但有時(shí)候你用的不是枚舉陶因,而是被一個(gè)又臭又長的構(gòu)造器給困住了。

animation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)

有多少 “timingFunction” 呢垂蜗?太多了好嘛楷扬。

一個(gè)不那么為人所知的事實(shí)是解幽,縮寫點(diǎn)符號(hào)對(duì)任何類型的任何 static 成員都有效。結(jié)合在 extension 中添加自定義 property 的能力烘苹,我們得到如下代碼…

extension CAMediaTimingFunction
{
    // 這個(gè)屬性會(huì)在第一次被訪問時(shí)初始化躲株。
    // (需要添加 @nonobjc 來防止編譯器
    //  給 static(或者 final)屬性生成動(dòng)態(tài)存取器。)
    @nonobjc static let EaseInEaseOut = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)

    // 另外一個(gè)選擇就是使用計(jì)算屬性, 它同樣很有效,
    // 但 *每次* 被訪問時(shí)都會(huì)重新求值:
    static var EaseInEaseOut: CAMediaTimingFunction {
        // .init 是 self.init 的簡寫
        return .init(name: kCAMediaTimingFunctionEaseInEaseOut)
    }
}

現(xiàn)在我們得到了一個(gè)優(yōu)雅的簡寫:

animation.timingFunction = .EaseInEaseOut

Context 中的重復(fù)標(biāo)識(shí)符

用來處理 Core Graphics Context镣衡、顏色空間等的代碼往往也是冗長的霜定。

CGContextSetFillColorWithColor(UIGraphicsGetCurrentContext(),
    CGColorCreate(CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB), [0.792, 0.792, 0.816, 1]))

再次使用棒棒的 extension

extension CGContext
{
    static func currentContext() -> CGContext? {
        return UIGraphicsGetCurrentContext()
    }
}

extension CGColorSpace
{
    static let GenericRGB = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB)
}

CGContextSetFillColorWithColor(.currentContext(),
    CGColorCreate(.GenericRGB, [0.792, 0.792, 0.816, 1]))

更簡單了是不?而且顯然會(huì)有更多的方式對(duì) Core Graphics 類型進(jìn)行擴(kuò)展廊鸥,以使其適應(yīng)你的需求望浩。

Auto Layout 中的重復(fù)標(biāo)識(shí)符

下面的代碼看起來熟悉么?

spaceConstraint = NSLayoutConstraint(
    item: label,
    attribute: .Leading,
    relatedBy: .Equal,
    toItem: button,
    attribute: .Trailing,
    multiplier: 1, constant: 20)
widthConstraint = NSLayoutConstraint(
    item: label,
    attribute: .Width,
    relatedBy: .LessThanOrEqual,
    toItem: nil,
    attribute: .NotAnAttribute,
    multiplier: 0, constant: 200)

spaceConstraint.active = true
widthConstraint.active = true

理解起來相當(dāng)困難惰说,是么磨德?Apple 認(rèn)識(shí)到這是個(gè)普遍存在的問題,所以重新設(shè)計(jì)了新的 NSLayoutAnchor API(在 iOS9 和 OS X 10.11 上適用)來處理這個(gè)問題:

spaceConstraint = label.leadingAnchor.constraintEqualToAnchor(button.trailingAnchor, constant: 20)
widthConstraint = label.widthAnchor.constraintLessThanOrEqualToConstant(200)
spaceConstraint.active = true
widthConstraint.active = true

然而吆视,我認(rèn)為還可以做的更好典挑。在我看來,下面的代碼比內(nèi)置的接口更容易閱讀和使用:

spaceConstraint = label.constrain(.Leading, .Equal, to: button, .Trailing, plus: 20)
widthConstraint = label.constrain(.Width, .LessThanOrEqual, to: 200)

// "設(shè)置 label 的 leading edge 和 button 的 trailing edge 相距 20"
// "設(shè)置 label 的 width 小于等于 200啦吧。"

上面的代碼是通過給 UIView 或者 NSView 添加一些 extension 來實(shí)現(xiàn)的您觉。這些輔助函數(shù)看起來可能會(huì)有些拙劣,但是用起來會(huì)特別方便授滓,而且很容易維護(hù)顾犹。(這里我已經(jīng)提供了另外一些包含默認(rèn)值的參數(shù)——一個(gè) multiplierpriorityidentifier ——所以你可以選擇更進(jìn)一步滴進(jìn)行自定義約束褒墨。)

extension UIView
{
    func constrain(
        attribute: NSLayoutAttribute,
        _ relation: NSLayoutRelation,
        to otherView: UIView,
        _ otherAttribute: NSLayoutAttribute,
        times multiplier: CGFloat = 1,
        plus constant: CGFloat = 0,
        atPriority priority: UILayoutPriority = UILayoutPriorityRequired,
        identifier: String? = nil)
        -> NSLayoutConstraint
    {
        let constraint = NSLayoutConstraint(
            item: self,
            attribute: attribute,
            relatedBy: relation,
            toItem: otherView,
            attribute: otherAttribute,
            multiplier: multiplier,
            constant: constant)
        constraint.priority = priority
        constraint.identifier = identifier
        constraint.active = true
        return constraint
    }
    
    func constrain(
        attribute: NSLayoutAttribute,
        _ relation: NSLayoutRelation,
        to constant: CGFloat,
        atPriority priority: UILayoutPriority = UILayoutPriorityRequired,
        identifier: String? = nil)
        -> NSLayoutConstraint
    {
        let constraint = NSLayoutConstraint(
            item: self,
            attribute: attribute,
            relatedBy: relation,
            toItem: nil,
            attribute: .NotAnAttribute,
            multiplier: 0,
            constant: constant)
        constraint.priority = priority
        constraint.identifier = identifier
        constraint.active = true
        return constraint
    }
}

你好~操作符

首先我必須提醒一下:如果要使用自定義操作符,一定要三思而后行擎宝。使用它們很簡單郁妈,但最終可能會(huì)得到一堆像屎一樣的代碼。一定要對(duì)代碼的健康性持懷疑態(tài)度绍申,然后你會(huì)發(fā)現(xiàn)在某些場景下噩咪,自定義操作符確實(shí)是很有用的。

重載它們

如果你有開發(fā)過拖動(dòng)手勢相關(guān)的功能极阅,你可能會(huì)寫過類似下面的代碼:

// 觸摸開始 / 鼠標(biāo)按下:

let touchPos = touch.locationInView(container)
objectOffset = CGPoint(x: object.center.x - touchPos.x, y: object.center.y - touchPos.y)

// 觸摸移動(dòng) / 鼠標(biāo)拖動(dòng):

let touchPos = touch.locationInView(container)
object.center = CGPoint(x: touchPos.x + objectOffset.x, y: touchPos.y + objectOffset.y)

在這段代碼里面我們只做了簡單的加法和減法胃碾,但因?yàn)?CGPoint 包含 xy,所以每個(gè)表達(dá)式我們都要寫兩次筋搏。所以我們需要一些簡化操作的函數(shù)仆百。

objectOffset 代表觸摸位置和對(duì)象位置的距離。描述這種距離最好的方式并不是 CGPoint奔脐,而是不那么為人所知的 CGVector俄周, 它不使用 xy吁讨,而是用 dxdy 來表示距離或者 “deltas“。

這里寫圖片描述
這里寫圖片描述

所以兩個(gè)點(diǎn)相減得到一個(gè)向量就比較符合邏輯了峦朗,這樣一來我們就得到了 - 操作符的一個(gè)重載:

/// - 返回: 從 `rhs` 到 `lhs`的向量建丧。
func -(lhs: CGPoint, rhs: CGPoint) -> CGVector
{
    return CGVector(dx: lhs.x - rhs.x, dy: lhs.y - rhs.y)
}

然后,相反滴波势,把一個(gè)向量和一個(gè)點(diǎn)相加得到另外一個(gè)點(diǎn):

// - 返回: `lhs` 偏移`rhs` 之后得到的一個(gè)點(diǎn)
func +(lhs: CGPoint, rhs: CGVector) -> CGPoint
{
    return CGPoint(x: lhs.x + rhs.dx, y: lhs.y + rhs.dy)
}

現(xiàn)在下面的代碼看起來就感覺很好了翎朱!

// 觸摸開始:
objectOffset = object.center - touch.locationInView(container)

// 觸摸移動(dòng):
object.center = touch.locationInView(container) + objectOffset

練習(xí):想一想其它可以用在點(diǎn)和向量上的操作符,并對(duì)它們進(jìn)行重載尺铣。
建議:-(CGPoint, CGVector)拴曲、*(CGVector, CGFloat)-(CGVector)

獨(dú)門絕技

下面是一些更有創(chuàng)造性的內(nèi)容迄埃。Swift 提供了一些復(fù)合賦值操作符疗韵,這些操作符在執(zhí)行某個(gè)操作的同時(shí)進(jìn)行賦值:

a += b   // 等價(jià)于 "a = a + b"
a %= b   // 等價(jià)于 "a = a % b"

但是仍然存在其它不包含內(nèi)置復(fù)合賦值形式的操作符。最常見的例子就是 ??侄非,空合并運(yùn)算符蕉汪。也就是 Ruby 中的 ||=,例如逞怨,首先實(shí)現(xiàn)只有在變量是 nil (或者不是)的情況下才賦值的版本者疤。這對(duì) Swift 中的可選值意義非凡,而且實(shí)現(xiàn)起來也很簡單:

infix operator ??= { associativity right precedence 90 assignment } // 匹配其它的賦值操作符

/// 如果 `lhs` 為 `nil`, 把 `rhs` 的值賦給它
func ??=<T>(inout lhs: T?, @autoclosure rhs: () -> T)
{
    lhs = lhs ?? rhs()
}

這段代碼看起來可能很復(fù)雜——這里我們做了下面幾件事情叠赦。

  • infix operator 聲明用來告訴 Swift 把 ??= 當(dāng)作一個(gè)操作符驹马。
  • 使用 <T> 將函數(shù)泛型化,從而使其可以支持任何類型的值除秀。
  • inout 表示允許修改左側(cè)的運(yùn)算數(shù)
  • @autoclosure 用來支持短路賦值糯累,有需要的話可以只對(duì)右側(cè)做賦值操作。(著也依賴于 ?? 本身對(duì)短路的支持册踩。)

但在我看來泳姐,上述代碼實(shí)現(xiàn)的功能是非常清晰而且易用的:

a ??= b   // 等價(jià)于 "a = a ?? b"

調(diào)度

關(guān)于如何在 Swift 中使用 GCD,最好的方式是閱讀官方文檔暂吉,但在本文中我仍然會(huì)介紹一些基礎(chǔ)知識(shí)點(diǎn)胖秒。如果想了解更多,請(qǐng)參照這份概要慕的。

Swift 2 引入了協(xié)議擴(kuò)展阎肝,因此,很多之前的全局標(biāo)準(zhǔn)庫函數(shù)變成了準(zhǔn)成員函數(shù):如 map(seq, transform) 變成了現(xiàn)在的 seq.map(transform)肮街,join(separator, seq) 變成了現(xiàn)在的 seq.joinWithSeparator(separator) 等等风题。這樣的話,那些嚴(yán)格說來不屬于類實(shí)例方法的函數(shù)仍然可以用 . 符號(hào)訪問,而且還減少了逗號(hào)(PS:原文為parentheses俯邓,可能是作者筆誤)的數(shù)目骡楼,從而不會(huì)把代碼弄得太亂

然而這種變化并沒有應(yīng)用到 Swift 標(biāo)準(zhǔn)庫外的自由函數(shù)稽鞭,比如 dispatch_async()UIImageJPEGRepresentation()鸟整。這些函數(shù)仍然很難用,如果你經(jīng)常使用它們朦蕴,還是很值得思考一下如何利用 Swift 來幫你改造一下它們篮条。下面是一些入門的 GCD 例子。

sync 或者非 sync

這些都很簡單吩抓;我們馬上開始:

extension dispatch_queue_t
{
    final func async(block: dispatch_block_t) {
        dispatch_async(self, block)
    }
    
    // 這里的 `block` 需要是 @noescape 的, 但不能是鏈接中這樣的: <http://openradar.me/19770770>
    final func sync(block: dispatch_block_t) {
        dispatch_sync(self, block)
    }
}

上面簡化的兩個(gè)函數(shù)直接調(diào)用了普通的調(diào)度函數(shù)涉茧,但可以讓我們通過 . 符號(hào)調(diào)用它們,這是我們之前做不到的疹娶。

注:GCD 對(duì)象是以一種古怪的方式導(dǎo)出到 Swift 的伴栓,盡管以類的方式也可以實(shí)現(xiàn),但實(shí)際上 dispatch_queue_t 只是一個(gè)協(xié)議而已雨饺。在這里我把兩個(gè)函數(shù)都標(biāo)注了 final 來表明我們的意圖:我們不希望在這里使用動(dòng)態(tài)調(diào)度钳垮,盡管在我看來這種情況下使用協(xié)議擴(kuò)展是很好的,但是不要在哪都用额港。

mySerialQueue.sync {
    print("I’m on the queue!")
    threadsafeNum++
}

dispatch_get_global_queue(QOS_CLASS_BACKGROUND, 0).async {
    expensivelyReticulateSplines()
    print("Done!")
    
    dispatch_get_main_queue().async {
        print("Back on the main queue.")
    }
}

更進(jìn)一步的 sync 的版本是從 Swift 標(biāo)準(zhǔn)庫函數(shù)中的 with* 家族獲取到的靈感饺窿,我們要做的事情是返回一個(gè)在閉包中計(jì)算得到的結(jié)果:

extension dispatch_queue_t
{
    final func sync<Result>(block: () -> Result) -> Result {
        var result: Result?
        dispatch_sync(self) {
            result = block()
        }
        return result!
    }
}

// 在串行隊(duì)列上抓取一些數(shù)據(jù)
let currentItems = mySerialQueue.sync {
    print("I’m on the queue!")
    return mutableItems.copy()
}

群體思維

另外兩個(gè)簡單的擴(kuò)展可以讓我們很好的使用 dispatch group

extension dispatch_queue_t
{
    final func async(group: dispatch_group_t, _ block: dispatch_block_t) {
        dispatch_group_async(group, self, block)
    }
}

extension dispatch_group_t
{
    final func waitForever() {
        dispatch_group_wait(self, DISPATCH_TIME_FOREVER)
    }
}

現(xiàn)在調(diào)用 async 的時(shí)候就可以包含一個(gè)額外的 group 參數(shù)了。

let group = dispatch_group_create()

concurrentQueue.async(group) {
    print("I’m part of the group")
}

concurrentQueue.async(group) {
    print("I’m independent, but part of the same group")
}

group.waitForever()
print("Everything in the group has now executed")

注:我們可以很簡單滴選擇 group.async(queue) 或者 queue.async(group)移斩。具體用哪個(gè)全看你自己——或者你甚至可以兩個(gè)都實(shí)現(xiàn)肚医。

優(yōu)雅的重定義

如果你的項(xiàng)目同時(shí)包含 Objective-C 和 Swift,你可能會(huì)碰到這種讓人頭大的情況:Obj-C 的 API 看起來不是那么 Swift 化向瓷。需要 NS_REFINED_FOR_SWIFT 來拯救我們了肠套。

在 Obj-C 中使用標(biāo)記了 (new in Xcode 7) 宏的函數(shù)、方法和變量是正常的猖任,但是導(dǎo)出到 Swift 之后糠排,它們會(huì)包含一個(gè) “__“前綴。

@interface MyClass : NSObject

/// @返回 @c 東西的下標(biāo), 如果沒有提供就返回 NSNotFound超升。
- (NSUInteger)indexOfThing:(id)thing NS_REFINED_FOR_SWIFT;

@end

// 當(dāng)導(dǎo)出到 Swift 時(shí), 它就變成了:

public class MyClass: NSObject
{
    public func __indexOfThing(thing: AnyObject) -> UInt
}

現(xiàn)在把 Obj-C 的方法放到一邊,你可以重用同樣的名字來提供一個(gè)更友好的 Swift 版本 API(實(shí)現(xiàn)方式通常是調(diào)用帶“__“前綴的原始版本):

extension MyClass
{
    /// - 返回: 給定 `thing` 的下標(biāo), 如果沒有就返回 `nil`哺徊。
    func indexOfThing(thing: AnyObject) -> Int?
    {
        let idx = Int(__indexOfThing(thing)) // 調(diào)用原始方法
        if idx == NSNotFound { return nil }
        return idx
    }
}

現(xiàn)在你可以心滿意足滴 “if let“了室琢!

更進(jìn)一步

Swift 還很年輕,它各個(gè)代碼庫的風(fēng)格也都是不同的落追。而大量的第三方微型庫也涌現(xiàn)出來盈滴,這些庫的代碼體現(xiàn)了作者在操作符,輔助函數(shù)和命名規(guī)范上所持的不同觀點(diǎn)。這種情況也就需要在處理依賴關(guān)系以及在團(tuán)隊(duì)中建立規(guī)范時(shí)更挑剔巢钓。

使用本文中技術(shù)最重要的原因不是寫最新潮和最酷炫的 Swift 代碼病苗。當(dāng)然,負(fù)責(zé)維護(hù)你代碼的人——也許是未來的你——可能會(huì)持不同的觀點(diǎn)症汹。為了他們硫朦,親愛的讀者,你需要在為了讓代碼變的清晰合理的情況下擴(kuò)展 Swift背镇,而不是為了讓代碼變的簡單而去擴(kuò)展它咬展。

譯者:Playground已經(jīng)上傳到github!

本文由 SwiftGG 翻譯組翻譯瞒斩,已經(jīng)獲得作者翻譯授權(quán)破婆,最新文章請(qǐng)?jiān)L問 http://swift.gg

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末胸囱,一起剝皮案震驚了整個(gè)濱河市祷舀,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌烹笔,老刑警劉巖裳扯,帶你破解...
    沈念sama閱讀 211,194評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異箕宙,居然都是意外死亡嚎朽,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,058評(píng)論 2 385
  • 文/潘曉璐 我一進(jìn)店門柬帕,熙熙樓的掌柜王于貴愁眉苦臉地迎上來哟忍,“玉大人,你說我怎么就攤上這事陷寝」埽” “怎么了?”我有些...
    開封第一講書人閱讀 156,780評(píng)論 0 346
  • 文/不壞的土叔 我叫張陵凤跑,是天一觀的道長爆安。 經(jīng)常有香客問我,道長仔引,這世上最難降的妖魔是什么扔仓? 我笑而不...
    開封第一講書人閱讀 56,388評(píng)論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮咖耘,結(jié)果婚禮上翘簇,老公的妹妹穿的比我還像新娘。我一直安慰自己儿倒,他們只是感情好版保,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,430評(píng)論 5 384
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般彻犁。 火紅的嫁衣襯著肌膚如雪叫胁。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,764評(píng)論 1 290
  • 那天汞幢,我揣著相機(jī)與錄音驼鹅,去河邊找鬼。 笑死急鳄,一個(gè)胖子當(dāng)著我的面吹牛谤民,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播疾宏,決...
    沈念sama閱讀 38,907評(píng)論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼张足,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了坎藐?” 一聲冷哼從身側(cè)響起为牍,我...
    開封第一講書人閱讀 37,679評(píng)論 0 266
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎岩馍,沒想到半個(gè)月后碉咆,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,122評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡蛀恩,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,459評(píng)論 2 325
  • 正文 我和宋清朗相戀三年疫铜,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片双谆。...
    茶點(diǎn)故事閱讀 38,605評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡壳咕,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出顽馋,到底是詐尸還是另有隱情谓厘,我是刑警寧澤,帶...
    沈念sama閱讀 34,270評(píng)論 4 329
  • 正文 年R本政府宣布寸谜,位于F島的核電站竟稳,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏熊痴。R本人自食惡果不足惜他爸,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,867評(píng)論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望果善。 院中可真熱鬧讲逛,春花似錦、人聲如沸岭埠。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,734評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽惜论。三九已至许赃,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間馆类,已是汗流浹背混聊。 一陣腳步聲響...
    開封第一講書人閱讀 31,961評(píng)論 1 265
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留乾巧,地道東北人句喜。 一個(gè)月前我還...
    沈念sama閱讀 46,297評(píng)論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像沟于,于是被迫代替她去往敵國和親咳胃。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,472評(píng)論 2 348

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

  • 發(fā)現(xiàn) 關(guān)注 消息 iOS 第三方庫旷太、插件展懈、知名博客總結(jié) 作者大灰狼的小綿羊哥哥關(guān)注 2017.06.26 09:4...
    肇東周閱讀 12,059評(píng)論 4 62
  • 關(guān)于 Swift 重要這個(gè)文檔所包含的準(zhǔn)備信息, 是關(guān)于開發(fā)的 API 和技術(shù)的。這個(gè)信息可能會(huì)改變, 根據(jù)這個(gè)文...
    無灃閱讀 4,283評(píng)論 1 27
  • 沉默島主閱讀 327評(píng)論 3 2
  • 早上供璧,沖了一杯淡淡的茉莉白茶存崖,靜靜地坐在書桌前,嗅了下暖暖的茶香睡毒,看向窗外来惧。外面有幾個(gè)十七八歲的少女正在嬉笑玩鬧,...
    那片星空下_d786閱讀 359評(píng)論 1 3
  • 01 ▼ 好朋友蓉在美甲店里做指甲,中間進(jìn)來一個(gè)電話偶房,因?yàn)椴环奖憬勇牫寐匕戳嗣馓幔娫捘嵌藗鱽硪欢蔚统恋哪兄幸簟?...
    開心女皇閱讀 372評(píng)論 0 0