我所理解的iOS runtime

從一下方面來深入研究:

理解面向?qū)ο蟮念惖矫嫦蜻^程的結(jié)構(gòu)體

?深入理解OC消息轉(zhuǎn)發(fā)機制

理解OC的屬性property

實踐Category添加屬性與黑魔法method swizzling

深入weak實現(xiàn)機理

runtime就是運行時,runtime很強大,是OC最重要的一部分也是OC最大的特色扮碧,可以不夸張的說runtime成就了OC,盡管runtime是OC的一個模塊而已哩治。

我們都知道高級編程語言想要成為可執(zhí)行文件需要先編譯為匯編語言再匯編為機器語言,機器語言也是計算機能夠識別的唯一語言衬鱼,但是OC并不能直接編譯為匯編語言业筏,而是要先轉(zhuǎn)寫為純C語言再進行編譯和匯編的操作,從OC到C語言的過渡就是由runtime來實現(xiàn)的鸟赫。然而我們使用OC進行面向?qū)ο箝_發(fā)蒜胖,而C語言更多的是面向過程開發(fā)消别,這就需要將面向?qū)ο蟮念愞D(zhuǎn)變?yōu)槊嫦蜻^程的結(jié)構(gòu)體,本文正是通過runtime源碼分析來講解runtime是如何將面向?qū)ο蟮念愞D(zhuǎn)變?yōu)槊嫦蜻^程的結(jié)構(gòu)體台谢。

深入代碼理解instance寻狂、class object、metaclass

面向?qū)ο缶幊讨信缶冢钪匾母拍罹褪穷惿呷旅嫖覀兙蛷拇a入手,看看OC是如何實現(xiàn)類的樊拓。

前文一直在說runtime將面向?qū)ο蟮念愞D(zhuǎn)變?yōu)槊嫦蜻^程的結(jié)構(gòu)體纠亚,那這個結(jié)構(gòu)體到底是什么樣子的?打開#import文件筋夏,可以發(fā)現(xiàn)以下幾行代碼


通過注釋和代碼不難發(fā)現(xiàn)蒂胞,我們創(chuàng)建的一個對象或?qū)嵗鋵嵕褪且粋€struct objc_object結(jié)構(gòu)體,而我們常用的id也就是這個結(jié)構(gòu)體的指針条篷。有如下代碼:


通過上述代碼可以看出骗随,我們創(chuàng)建的NSString類的實例str其實就是一個struct objc_object結(jié)構(gòu)體指針,所以不管是Foundation框架中的類或是自定義的類赴叹,我們創(chuàng)建的類的實例最終獲取的都是一個結(jié)構(gòu)體指針鸿染,這個結(jié)構(gòu)體只有一個成員變量就是Class類型的isa指針,Class是結(jié)構(gòu)體指針乞巧,指向struct objc_class牡昆,那這個結(jié)構(gòu)體又是什么呢?這里先透露一句話str is a NSString摊欠,再加上Class這個指針的名字,我們不難猜測柱宦,Class就是代表NSString這個類些椒。

接下來會詳細講解這個結(jié)構(gòu)體,現(xiàn)在再看另一個例子掸刊,有時我們也會通過下述方法來創(chuàng)建一個實例:


可能你已經(jīng)發(fā)現(xiàn)了免糕,通過實例對象調(diào)用的class方法,我們能夠獲取到一個Class類型的變量忧侧,我們可以通過這個Class來創(chuàng)建相應(yīng)的實例對象石窑。

實際上,OC中的類也是一個對象蚓炬,稱為類對象松逊,上述方法中通過[str class]方法獲取到的就是NSString類的類對象,接著我們就可以通過這個類對象來創(chuàng)建實例對象肯夏,那這個類對象又是什么東西呢经宏?打開#import文件犀暑,我們可以找到結(jié)構(gòu)體struct objc_class的定義,該結(jié)構(gòu)體定義如下:


struct objc_class結(jié)構(gòu)體定義了很多變量烁兰,通過命名不難發(fā)現(xiàn)耐亏,結(jié)構(gòu)體里保存了指向父類的指針、類的名字沪斟、版本广辰、實例大小、實例變量列表主之、方法列表择吊、緩存、遵守的協(xié)議列表等杀餐,一個類包含的信息也不就正是這些嗎干发?沒錯,類對象就是一個結(jié)構(gòu)體struct objc_class史翘,這個結(jié)構(gòu)體存放的數(shù)據(jù)稱為元數(shù)據(jù)(metadata)枉长,該結(jié)構(gòu)體的第一個成員變量也是isa指針,這就說明了Class本身其實也是一個對象琼讽,因此我們稱之為類對象必峰,類對象在編譯期產(chǎn)生用于創(chuàng)建實例對象,是單例钻蹬。

類對象中的元數(shù)據(jù)存儲的都是如何創(chuàng)建一個實例的相關(guān)信息吼蚁,那么類對象和類方法應(yīng)該從哪里創(chuàng)建呢?就是從isa指針指向的結(jié)構(gòu)體創(chuàng)建问欠,類對象的isa指針指向的我們稱之為元類(metaclass)肝匆,元類中保存了創(chuàng)建類對象以及類方法所需的所有信息,因此整個結(jié)構(gòu)應(yīng)該如下圖所示:

實例對象顺献、類對象與元類簡圖

通過上圖我們可以清晰的看出來一個實例對象也就是struct objc_object結(jié)構(gòu)體它的isa指針指向類對象旗国,類對象的isa指針指向了元類,super_class指針指向了父類的類對象注整,而元類的super_class指針指向了父類的元類能曾,那元類的isa指針又指向了什么?為了更清晰的表達直接使用一個大神畫的圖肿轨。

實例對象寿冕、類對象與元類的自閉環(huán)

通過上圖我們可以看出整個體系構(gòu)成了一個自閉環(huán),如果是從NSObject中繼承而來的上圖中的Root class就是NSObject椒袍。至此驼唱,整個實例、類對象驹暑、元類的概念也就講清了曙蒸,接下來我們在代碼中看看這些概念該怎么應(yīng)用捌治。


c1是通過一個實例對象獲取的Class,實例對象可以獲取到其類對象纽窟,類名作為消息的接受者時代表的是類對象肖油,因此類對象獲取Class得到的是其本身,同時也印證了類對象是一個單例的想法臂港。

那么如果我們想獲取isa指針的指向?qū)ο竽兀?/p>

介紹兩個函數(shù)


class_isMetaClass用于判斷Class對象是否為元類森枪,object_getClass用于獲取對象的isa指針指向的對象。

再看如下代碼:


通過代碼可以看出审孽,一個實例對象通過class方法獲取的Class就是它的isa指針指向的類對象县袱,而類對象不是元類,類對象的isa指針指向的對象是元類佑力。



你不知道的msg_send

我們知道在OC中的實例對象調(diào)用一個方法稱作消息傳遞式散,比如有如下代碼:


上述代碼中的第二句str稱為消息的接受者,appendString:稱作選擇子也就是我們常用的selector打颤,selector和參數(shù)共同構(gòu)成了消息暴拄,所以第二句話可以理解為將消息:"增加一個字符串: is a good guy"發(fā)送給消息的接受者str。

OC中里的消息傳遞采用動態(tài)綁定機制來決定具體調(diào)用哪個方法编饺,OC的實例方法在轉(zhuǎn)寫為C語言后實際就是一個函數(shù)乖篷,但是OC并不是在編譯期決定調(diào)用哪個函數(shù),而是在運行期決定透且,因為編譯期根本不能確定最終會調(diào)用哪個函數(shù)撕蔼,這是由于運行期可以修改方法的實現(xiàn),在后文會有講解秽誊。舉個栗子鲸沮,有如下代碼:


上述代碼在編譯期沒有任何問題,因為id類型可以指向任何類型的實例對象锅论,NSString有一個方法appendString:诉探,在編譯期不確定這個num到底具體指代什么類型的實例對象,并且在運行期還可以給NSNumber類型添加新的方法棍厌,因此編譯期發(fā)現(xiàn)有appendString:的函數(shù)聲明就不會報錯,但在運行時找不到在NSNumber類中找不到appendString:方法竖席,就會報錯耘纱。這也就是消息傳遞的強大之處和弊端,編譯期無法檢查到未定義的方法毕荐,運行期可以添加新的方法束析。

講了這么多OC究竟是怎么將實例方法轉(zhuǎn)換為C語言的函數(shù),又是如何調(diào)用這些函數(shù)的呢憎亚?這些都依靠強大的runtime员寇。

在深入代碼之前介紹一個clang編譯器的命令:


通過上述clang命令可以轉(zhuǎn)寫代碼弄慰,然后找到如下定義:

可以發(fā)現(xiàn)轉(zhuǎn)寫后的C語言代碼將實例方法轉(zhuǎn)寫為了一個靜態(tài)函數(shù)。接下來一行一行的分析上述代碼蝶锋,第一行代碼可以簡要表示為如下代碼:


這一行代碼做了三件事情陆爽,第一獲取Person類,第二注冊alloc方法扳缕,第三發(fā)送消息慌闭,將消息alloc發(fā)送給類對象,可以簡單的將注冊方法理解為躯舔,通過方法名獲取到轉(zhuǎn)寫后C語言函數(shù)的函數(shù)指針驴剔。

第二行代碼就可以簡寫為如下代碼:


這一行代碼與上一行類似,注冊了init方法粥庄,然后通過objc_msgSend函數(shù)將消息init發(fā)送給消息的接受者p丧失。

第三行是一個對setter的調(diào)用,同樣的也可以簡寫為如下代碼:


這一行代碼同樣是先注冊方法setName:然后通過objc_msgSend函數(shù)將消息setName:發(fā)送給消息的接收者惜互,只是多了一個參數(shù)的傳遞布讹。

同理,最后一行代碼也可以簡寫為如下:


解釋與上述相同载佳,不再贅述炒事。

到這里,我們應(yīng)該就可以看出OC的runtime通過objc_msgSend函數(shù)將一個面向?qū)ο蟮南鬟f轉(zhuǎn)為了面向過程的函數(shù)調(diào)用蔫慧。

objc_msgSend函數(shù)根據(jù)消息的接受者和selector選擇適當?shù)姆椒▉碚{(diào)用挠乳,那它又是如何選擇的呢?這再來回顧一下幾個主要的結(jié)構(gòu)體:


注意結(jié)構(gòu)體struct objc_class中包含一個成員變量struct objc_method_list **methodLists姑躲,通過名稱我們分析出這個成員變量保存了實例方法列表睡扬,繼續(xù)查找結(jié)構(gòu)體struct objc_method_list的定義如下:

我們發(fā)現(xiàn)struct objc_method_list中還包含了一個未知的結(jié)構(gòu)體struct _objc_method同時也找到它的定義,為了方便查看將兩者寫在一起黍析。

結(jié)構(gòu)體struct objc_method_list里面包含以下幾個成員變量:結(jié)構(gòu)體struct _objc_method的大小卖怜、方法個數(shù)以及最重要的方法列表,方法列表存儲的是方法描述結(jié)構(gòu)體struct _objc_method阐枣,該結(jié)構(gòu)體里保存了選擇子马靠、方法類型以及方法的具體實現(xiàn)“剑可以看出方法的具體實現(xiàn)就是一個函數(shù)指針甩鳄,也就是我們自定義的實例方法,選擇子也就是selector可以理解為是一個字符串類型的名稱额划,用于查找對應(yīng)的函數(shù)實現(xiàn)(由于蘋果沒有開源selector的相關(guān)代碼妙啃,但是可以查到GNU OC中關(guān)于selector的定義,也是一個結(jié)構(gòu)體但是結(jié)構(gòu)體里存儲的就是一個字符串類型的名稱)。

這樣就能解釋objc_msgSend的工作原理的揖赴,為了匹配消息的接收者和選擇子馆匿,需要在消息的接收者所在的類中去搜索這個struct objc_method_list方法列表,如果能找到就可以直接跳轉(zhuǎn)到相關(guān)的具體實現(xiàn)中去調(diào)用燥滑,如果找不到渐北,那就會通過super_class指針沿著繼承樹向上去搜索,如果找到就跳轉(zhuǎn)突倍,如果到了繼承樹的根部(通常為NSObject)還沒有找到腔稀,那就會調(diào)用NSObjec的一個方法doesNotRecognizeSelector:,這個方法就會報unrecognized selector錯誤(其實在調(diào)用這個方法之前還會進行消息轉(zhuǎn)發(fā)羽历,還有三次機會來處理焊虏,消息轉(zhuǎn)發(fā)在后文會有介紹)。

這樣一看秕磷,要發(fā)送消息真的好復雜诵闭,需要經(jīng)過這么多步驟,難道不會影響性能嗎澎嚣?當然了疏尿,這樣一次次搜索和靜態(tài)綁定那樣直接跳轉(zhuǎn)到函數(shù)指針指向的位置去執(zhí)行來比肯定是耗時很多的,因此易桃,類對象也就是結(jié)構(gòu)體struct objc_class中有一個成員變量struct objc_cache褥琐,這個緩存里緩存的正是搜索方法的匹配結(jié)果,這樣在第二次及以后再訪問時就可以采用映射的方式找到相關(guān)實現(xiàn)的具體位置晤郑。

到這里我們就已經(jīng)弄清楚了整個發(fā)送消息的過程敌呈,但是當對象無法接收相關(guān)消息時又會發(fā)生什么?以及前文說的三次機會又是什么造寝?下文將會介紹消息轉(zhuǎn)發(fā)磕洪。

消息轉(zhuǎn)發(fā): unrecognized selector的最后三次機會


前文介紹了進行一次發(fā)送消息會在相關(guān)的類對象中搜索方法列表,如果找不到則會沿著繼承樹向上一直搜索知道繼承樹根部(通常為NSObject)诫龙,如果還是找不到并且消息轉(zhuǎn)發(fā)都失敗了就回執(zhí)行doesNotRecognizeSelector:方法報unrecognized selector錯析显。那么消息轉(zhuǎn)發(fā)到底是什么呢?接下來將會逐一介紹最后的三次機會签赃。

第一次機會: 所屬類動態(tài)方法解析

首先谷异,如果沿繼承樹沒有搜索到相關(guān)方法則會向接收者所屬的類進行一次請求,看是否能夠動態(tài)的添加一個方法锦聊,注意這是一個類方法歹嘹,因為是向接收者所屬的類進行請求。


第二次機會: 備援接收者


第三次機會: 消息重定向


理解OC的屬性property括丁,主要從runtime出發(fā)講解屬性property相關(guān)的底層實現(xiàn)和相關(guān)方法,

本文將會講解一些runtime操作屬性的相關(guān)方法伶选。

首先回顧一下相關(guān)代碼以及與property底層實現(xiàn)相關(guān)的兩個結(jié)構(gòu)體:


通過上述代碼其實我們可以看出史飞,一個@property屬性在底層就是一個結(jié)構(gòu)體描述尖昏,那么我們?nèi)绾潍@取這個結(jié)構(gòu)體呢?可以通過如下代碼獲取:


首先看一下objc_property_t是什么构资,在objc/runtime.h中可以找到相關(guān)定義:

typedefstructobjc_property*objc_property_t;

它是一個指向結(jié)構(gòu)體struct objc_property的指針抽诉,這里的結(jié)構(gòu)體struct objc_property其實就是前文中.cpp文件中的struct _prop_t結(jié)構(gòu)體,通過class_copyPropertyList方法就可以獲取到相關(guān)類的所有屬性吐绵,具體函數(shù)聲明如下:


注釋可以看出迹淌,第一個參數(shù)是相關(guān)類的類對象(如有疑問可以查閱本系列文章的前兩篇文章),第二個參數(shù)是一個指向unsigned int的指針己单,用于指明property的數(shù)量唉窃,通過該方法就能夠獲取到所有的屬性,接下來可以通過property_getName和property_getAttributes方法獲取該屬性描述的name和attributes值纹笼,輸出的結(jié)果如下:


name很好理解纹份,后面的attributes通過對比不難發(fā)現(xiàn)其規(guī)律,感興趣的讀者也可以多設(shè)置幾個不同類型廷痘、不同修飾符的property看一下輸出蔓涧。

除此之外哈有一下幾個方法用于根據(jù)屬性名獲取一個屬性描述結(jié)構(gòu)體、添加屬性笋额、替換屬性等方法元暴。


舉個簡單的栗子:


通過上述方法就能添加一個屬性,由于本人水平有限實際開發(fā)中沒有用過上述方法兄猩,具體實際例子也舉不出來所以不再過多贅述茉盏。

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

如果我們想為系統(tǒng)的類添加一個方法可以采用類別的方式進行擴展,相對來說比較簡單厦滤,但如果要添加一個屬性或稱為成員變量援岩,通常采用的方法就是繼承,這樣就比較繁瑣了掏导,如果不想去繼承那就可以通過runtime來進行關(guān)聯(lián)對象操作享怀。

使用runtime的關(guān)聯(lián)對象添加屬性與我們自定義類時定義的屬性其實是兩個不同的概念,通過關(guān)聯(lián)對象添加屬性本質(zhì)上是使用類別進行擴展趟咆,通過添加setter和getter方法從而在訪問時可以使用點語法進行方法添瓷,在使用上與自定義類定義的屬性沒有區(qū)別。

具體需要使用的C函數(shù)如下:


通過注釋和函數(shù)名不難發(fā)現(xiàn)上訴三個方法分別是設(shè)置關(guān)聯(lián)對象值纱、獲取關(guān)聯(lián)對象和刪除關(guān)聯(lián)對象鳞贷。

需要說明一下objc_AssociationPolicy,具體的定義如下:


這些關(guān)鍵詞很眼熟虐唠,沒錯搀愧,就是property使用的修飾符,具體含義也與property修飾符相同,

說了這么多咱筛,接下來舉個具體的栗子搓幌,為一個已有類添加一個關(guān)聯(lián)對象。


這個栗子設(shè)置的關(guān)聯(lián)對象其實沒有任何實際意義迅箩,通過代碼可以看出溉愁,使用runtime為一個已有類添加屬性就是通過類別擴展getter和setter方法。

實例方法

在本系列文章的第二篇iOS runtime探究(二): 從runtime開始深入理解OC消息轉(zhuǎn)發(fā)機制饲趋,我們詳細介紹了runtime對方法的底層處理拐揭,以及發(fā)送消息和消息轉(zhuǎn)發(fā)機制,這里就不再贅述了奕塑,如有需要可以查看相關(guān)文章堂污,本文會介紹OC層面對方法的相關(guān)操作,同時會介紹method swizzling的方法爵川。

先來回顧一下實例方法相關(guān)的結(jié)構(gòu)體和底層實現(xiàn)敷鸦,有如下代碼:




weak

weak不論是用作property修飾符還是用來修飾一個變量的聲明其作用是一樣的,就是不增加新對象的引用計數(shù)寝贡,被釋放時也不會減少新對象的引用計數(shù)扒披,同時在新對象被銷毀時,weak修飾的屬性或變量均會被設(shè)置為nil圃泡,這樣可以防止野指針錯誤碟案,本文要講解的也正是這個特性,runtime如何將weak修飾的變量的對象在銷毀時自動置為nil颇蜡。

那么runtime是如何實現(xiàn)在weak修飾的變量的對象在被銷毀時自動置為nil的呢价说?一個普遍的解釋是:runtime對注冊的類會進行布局,對于weak修飾的對象會放入一個hash表中风秤。用weak指向的對象內(nèi)存地址作為key鳖目,當此對象的引用計數(shù)為0的時候會dealloc,假如weak指向的對象內(nèi)存地址是a缤弦,那么就會以a為鍵在這個weak表中搜索领迈,找到所有以a為鍵的weak對象,從而設(shè)置為nil碍沐。

了解了以上知識后就可以深入runtiem代碼來看看具體實現(xiàn)細節(jié)狸捅,有興趣的讀者可以繼續(xù)閱讀。

深入runtime理解weak

這部分內(nèi)容參考《Objective-C高級編程:iOS與OS X多線程和內(nèi)存管理》累提,可以看出具體的實現(xiàn)方式就是使用了一個HashTable尘喝。

NSString*name = [[NSStringalloc] initWithString:@"Jiaming Chen"];

__weakNSString*weakStr = name;

當為weakStr這一weak類型的對象賦值時,編譯器會根據(jù)name的地址為key去查找weak哈希表斋陪,該表項的值為一個數(shù)組朽褪,將weakStr對象的地址加入到數(shù)組中置吓,當name變量超出變量作用域或引用計數(shù)為0時,會執(zhí)行dealloc函數(shù)缔赠,在執(zhí)行該函數(shù)時交洗,編譯器會以name變量的地址去查找weak哈希表的值,并將數(shù)組里所有weak對象全部賦值為nil橡淑。

本文出自

鏈接:http://www.reibang.com/p/4a32fb8648a3

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市咆爽,隨后出現(xiàn)的幾起案子梁棠,更是在濱河造成了極大的恐慌,老刑警劉巖斗埂,帶你破解...
    沈念sama閱讀 219,427評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件符糊,死亡現(xiàn)場離奇詭異,居然都是意外死亡呛凶,警方通過查閱死者的電腦和手機男娄,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,551評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事键兜∧┕海” “怎么了?”我有些...
    開封第一講書人閱讀 165,747評論 0 356
  • 文/不壞的土叔 我叫張陵笋庄,是天一觀的道長。 經(jīng)常有香客問我,道長实夹,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,939評論 1 295
  • 正文 為了忘掉前任粒梦,我火速辦了婚禮亮航,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘匀们。我一直安慰自己缴淋,他們只是感情好,可當我...
    茶點故事閱讀 67,955評論 6 392
  • 文/花漫 我一把揭開白布昼蛀。 她就那樣靜靜地躺著宴猾,像睡著了一般。 火紅的嫁衣襯著肌膚如雪叼旋。 梳的紋絲不亂的頭發(fā)上仇哆,一...
    開封第一講書人閱讀 51,737評論 1 305
  • 那天,我揣著相機與錄音夫植,去河邊找鬼讹剔。 笑死油讯,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的延欠。 我是一名探鬼主播陌兑,決...
    沈念sama閱讀 40,448評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼由捎!你這毒婦竟也來了兔综?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,352評論 0 276
  • 序言:老撾萬榮一對情侶失蹤狞玛,失蹤者是張志新(化名)和其女友劉穎软驰,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體心肪,經(jīng)...
    沈念sama閱讀 45,834評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡锭亏,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,992評論 3 338
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了硬鞍。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片慧瘤。...
    茶點故事閱讀 40,133評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖固该,靈堂內(nèi)的尸體忽然破棺而出锅减,到底是詐尸還是另有隱情,我是刑警寧澤伐坏,帶...
    沈念sama閱讀 35,815評論 5 346
  • 正文 年R本政府宣布上煤,位于F島的核電站,受9級特大地震影響著淆,放射性物質(zhì)發(fā)生泄漏劫狠。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,477評論 3 331
  • 文/蒙蒙 一永部、第九天 我趴在偏房一處隱蔽的房頂上張望独泞。 院中可真熱鬧,春花似錦苔埋、人聲如沸懦砂。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,022評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽荞膘。三九已至,卻和暖如春玉工,著一層夾襖步出監(jiān)牢的瞬間羽资,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,147評論 1 272
  • 我被黑心中介騙來泰國打工遵班, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留屠升,地道東北人潮改。 一個月前我還...
    沈念sama閱讀 48,398評論 3 373
  • 正文 我出身青樓,卻偏偏與公主長得像腹暖,于是被迫代替她去往敵國和親汇在。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,077評論 2 355

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

  • 轉(zhuǎn)至元數(shù)據(jù)結(jié)尾創(chuàng)建: 董瀟偉脏答,最新修改于: 十二月 23, 2016 轉(zhuǎn)至元數(shù)據(jù)起始第一章:isa和Class一....
    40c0490e5268閱讀 1,721評論 0 9
  • 主要參考鏈接: http://yulingtianxia.com/blog/2014/11/05/objectiv...
    Kevin_Junbaozi閱讀 3,313評論 0 10
  • 參考鏈接: http://www.cnblogs.com/ioshe/p/5489086.html 簡介 Runt...
    樂樂的簡書閱讀 2,136評論 0 9
  • 這篇文章完全是基于南峰子老師博客的轉(zhuǎn)載 這篇文章完全是基于南峰子老師博客的轉(zhuǎn)載 這篇文章完全是基于南峰子老師博客的...
    西木閱讀 30,560評論 33 466
  • 現(xiàn)在的感覺耳朵被罩住糕殉,聲音進不去。似乎一個小的罩子殖告,把腦袋的上半部分罩住了糙麦,腦子轉(zhuǎn)不起來,聲音聽不進去丛肮,整個人蒙蒙...
    by_10閱讀 237評論 0 1