多線程之Runloop

Runloop
實(shí)現(xiàn)了線程內(nèi)部的事件循環(huán)常侣。一個(gè)線程通常一次只能執(zhí)行一個(gè)任務(wù)蜡饵,任務(wù)執(zhí)行完成線程就會(huì)退出,但是有時(shí)候胳施,我們希望線程能夠一直處理事件不退出溯祸,或者說(shuō)有事件就執(zhí)行,沒(méi)事件就等待事件巾乳。就比如主線程您没,app能隨時(shí)接收消息鸟召,處于接收消息 -> 等待 -> 處理的循環(huán)中

Runloop與線程的關(guān)系
1胆绊、Runloop與線程一一對(duì)應(yīng)
2、線程創(chuàng)建時(shí)沒(méi)有Runloop欧募,Runloop的創(chuàng)建發(fā)生在第一次獲取時(shí)压状,類似懶加載
3、Runloop的銷毀發(fā)生在線程結(jié)束時(shí)
4跟继、主線程的Runloop默認(rèn)開啟
5种冬、Runloop用于管理線程中要處理的事件和消息,并有一個(gè)入口函數(shù)舔糖,在線程執(zhí)行完后能一直處于接收消息 -> 等待 -> 處理的循環(huán)中娱两,直至線程結(jié)束

iOS中的NSRunloop和CFRunloopRef
Cocoa中的NSRunLoop類并不是線程安全的,我們不能在一個(gè)線程中去操作另外一個(gè)線程的run loop對(duì)象金吗,那很可能會(huì)造成意想不到的后果十兢。不過(guò)幸運(yùn)的是CoreFundation中的不透明類CFRunLoopRef是線程安全的趣竣,而且兩種類型的run loop完全可以混合使用。Cocoa中的NSRunLoop類可以通過(guò)實(shí)例方法:

  • (CFRunLoopRef)getCFRunLoop;
    獲取對(duì)應(yīng)的CFRunLoopRef類旱物,來(lái)達(dá)到線程安全的目的

Runloop如何工作

Runloop的內(nèi)部結(jié)構(gòu)
  • CoreFoundation下的Runloop
typedef CFStringRef CFRunLoopMode CF_EXTENSIBLE_STRING_ENUM;

typedef struct CF_BRIDGED_MUTABLE_TYPE(id) __CFRunLoop * CFRunLoopRef;

typedef struct CF_BRIDGED_MUTABLE_TYPE(id) __CFRunLoopSource * CFRunLoopSourceRef;

typedef struct CF_BRIDGED_MUTABLE_TYPE(id) __CFRunLoopObserver * CFRunLoopObserverRef;

typedef struct CF_BRIDGED_MUTABLE_TYPE(NSTimer) __CFRunLoopTimer * CFRunLoopTimerRef;
struct __CFRunLoopMode {
    CFStringRef _name;            // Mode Name, 例@"kCFRunLoopDefaultMode"
    CFMutableSetRef _sources0;    // Set
    CFMutableSetRef _sources1;    // Set
    CFMutableArrayRef _observers; // Array
    CFMutableArrayRef _timers;    // Array
    ...
};
  
struct __CFRunLoop {
    CFMutableSetRef _commonModes;     // Set
    CFMutableSetRef _commonModeItems; // Set
    CFRunLoopModeRef _currentMode;    // Current Runloop Mode
    CFMutableSetRef _modes;           // Set
    ...
};
  • Runloop的Mode
    我們都知道Runloop有幾個(gè)常用的模式遥缕,比如:NSDefaultRunLoopMode\NSRunLoopCommonModes\UITrackingRunLoopMode,每個(gè)Mode包含一組source\observer\timer, 又稱為Mode item宵呛,要知道不同mode的下单匣,Runloop的工作機(jī)制是不完全相同的,所以宝穗,每個(gè)Runloop對(duì)象中會(huì)有很多mode
    1户秤、Runloop啟動(dòng)時(shí),只能選擇一個(gè)mode進(jìn)行配置
    2逮矛、當(dāng)mode要切換虎忌,Runloop會(huì)退出,選擇新的mode重新啟動(dòng)橱鹏,這樣做主要是為了分隔開不同組的 Source/Timer/Observer膜蠢,讓其互不影響
    3、每個(gè)mode會(huì)有一個(gè)name用于區(qū)分莉兰,一組observer觀察Runloop的狀態(tài)挑围,另外就是事件源sources和timer,封裝事件消息
    4糖荒、當(dāng)mode切換時(shí)杉辙,Runloop會(huì)將commonModeItems的source\observer\timer,同步到具有common屬性的mode中

  • CFRunLoopSourceRef 是事件產(chǎn)生的地方捶朵。Source有兩個(gè)版本:Source0 和 Source1蜘矢。
    Source0 只包含了一個(gè)回調(diào)(函數(shù)指針),它并不能主動(dòng)觸發(fā)事件综看。使用時(shí)品腹,你需要先調(diào)用 CFRunLoopSourceSignal(source),將這個(gè) Source 標(biāo)記為待處理红碑,然后手動(dòng)調(diào)用 CFRunLoopWakeUp(runloop) 來(lái)喚醒 RunLoop舞吭,讓其處理這個(gè)事件。
    Source1 包含了一個(gè) mach_port 和一個(gè)回調(diào)(函數(shù)指針)析珊,被用于通過(guò)內(nèi)核和其他線程相互發(fā)送消息羡鸥。這種 Source 能主動(dòng)喚醒 RunLoop 的線程,其原理在下面會(huì)講到忠寻。

  • CFRunLoopTimerRef 是基于時(shí)間的觸發(fā)器惧浴,它和 NSTimer 是toll-free bridged 的,可以混用奕剃。其包含一個(gè)時(shí)間長(zhǎng)度和一個(gè)回調(diào)(函數(shù)指針)衷旅。當(dāng)其加入到 RunLoop 時(shí)哑姚,RunLoop會(huì)注冊(cè)對(duì)應(yīng)的時(shí)間點(diǎn),當(dāng)時(shí)間點(diǎn)到時(shí)芜茵,RunLoop會(huì)被喚醒以執(zhí)行那個(gè)回調(diào)叙量。

  • CFRunLoopObserverRef 是觀察者,每個(gè) Observer 都包含了一個(gè)回調(diào)(函數(shù)指針)九串,當(dāng) RunLoop 的狀態(tài)發(fā)生變化時(shí)绞佩,觀察者就能通過(guò)回調(diào)接受到這個(gè)變化≈砼ィ可以觀測(cè)的時(shí)間點(diǎn)有以下幾個(gè):

/* Run Loop Observer Activities */
 typedef CF_OPTIONS(CFOptionFlags, CFRunLoopActivity) {
    kCFRunLoopEntry = (1UL << 0),
    kCFRunLoopBeforeTimers = (1UL << 1),
    kCFRunLoopBeforeSources = (1UL << 2),
    kCFRunLoopBeforeWaiting = (1UL << 5),
    kCFRunLoopAfterWaiting = (1UL << 6),
    kCFRunLoopExit = (1UL << 7),
    kCFRunLoopAllActivities = 0x0FFFFFFFU
};
20130703215237531.png
1432798974517485.png

應(yīng)用場(chǎng)景舉例:主線程的 RunLoop 里有兩個(gè)預(yù)置的 Mode:kCFRunLoopDefaultMode 和 UITrackingRunLoopMode品山。這兩個(gè) Mode 都已經(jīng)被標(biāo)記為"Common"屬性。DefaultMode 是 App 平時(shí)所處的狀態(tài)烤低,TrackingRunLoopMode 是追蹤 ScrollView 滑動(dòng)時(shí)的狀態(tài)肘交。當(dāng)你創(chuàng)建一個(gè) Timer 并加到 DefaultMode 時(shí),Timer 會(huì)得到重復(fù)回調(diào)扑馁,但此時(shí)滑動(dòng)一個(gè)TableView時(shí)涯呻,RunLoop 會(huì)將 mode 切換為 TrackingRunLoopMode,這時(shí) Timer 就不會(huì)被回調(diào)腻要,并且也不會(huì)影響到滑動(dòng)操作复罐。

有時(shí)你需要一個(gè) Timer,在兩個(gè) Mode 中都能得到回調(diào)雄家,一種辦法就是將這個(gè) Timer 分別加入這兩個(gè) Mode效诅。還有一種方式,就是將 Timer 加入到頂層的 RunLoop 的 "commonModeItems" 中趟济。"commonModeItems" 被 RunLoop 自動(dòng)更新到所有具有"Common"屬性的 Mode 里去乱投。

CFRunLoop對(duì)外暴露的管理 Mode 接口只有下面2個(gè):

CFRunLoopAddCommonMode(CFRunLoopRef runloop, CFStringRef modeName);
CFRunLoopRunInMode(CFStringRef modeName, ...);

Mode 暴露的管理 mode item 的接口有下面幾個(gè):

CFRunLoopAddSource(CFRunLoopRef rl, CFRunLoopSourceRef source, CFStringRef modeName);
CFRunLoopAddObserver(CFRunLoopRef rl, CFRunLoopObserverRef observer, CFStringRef modeName);
CFRunLoopAddTimer(CFRunLoopRef rl, CFRunLoopTimerRef timer, CFStringRef mode);
CFRunLoopRemoveSource(CFRunLoopRef rl, CFRunLoopSourceRef source, CFStringRef modeName);
CFRunLoopRemoveObserver(CFRunLoopRef rl, CFRunLoopObserverRef observer, CFStringRef modeName);
CFRunLoopRemoveTimer(CFRunLoopRef rl, CFRunLoopTimerRef timer, CFStringRef mode);

你只能通過(guò) mode name 來(lái)操作內(nèi)部的 mode,當(dāng)你傳入一個(gè)新的 mode name 但 RunLoop 內(nèi)部沒(méi)有對(duì)應(yīng) mode 時(shí)顷编,RunLoop會(huì)自動(dòng)幫你創(chuàng)建對(duì)應(yīng)的 CFRunLoopModeRef戚炫。對(duì)于一個(gè) RunLoop 來(lái)說(shuō),其內(nèi)部的 mode 只能增加不能刪除勾效。

蘋果公開提供的 Mode 有兩個(gè):kCFRunLoopDefaultMode (NSDefaultRunLoopMode) 和 UITrackingRunLoopMode嘹悼,你可以用這兩個(gè) Mode Name 來(lái)操作其對(duì)應(yīng)的 Mode叛甫。

同時(shí)蘋果還提供了一個(gè)操作 Common 標(biāo)記的字符串:kCFRunLoopCommonModes (NSRunLoopCommonModes)层宫,你可以用這個(gè)字符串來(lái)操作 Common Items,或標(biāo)記一個(gè) Mode 為 "Common"其监。使用時(shí)注意區(qū)分這個(gè)字符串和其他 mode name萌腿。

AutoreleasePool
App啟動(dòng)后,蘋果在主線程 RunLoop 里注冊(cè)了兩個(gè) Observer抖苦,其回調(diào)都是 _wrapRunLoopWithAutoreleasePoolHandler()毁菱。
第一個(gè) Observer 監(jiān)視的事件是 Entry(即將進(jìn)入Loop)米死,其回調(diào)內(nèi)會(huì)調(diào)用 _objc_autoreleasePoolPush() 創(chuàng)建自動(dòng)釋放池。其 order 是-2147483647贮庞,優(yōu)先級(jí)最高峦筒,保證創(chuàng)建釋放池發(fā)生在其他所有回調(diào)之前。
第二個(gè) Observer 監(jiān)視了兩個(gè)事件: BeforeWaiting(準(zhǔn)備進(jìn)入休眠) 時(shí)調(diào)用_objc_autoreleasePoolPop() 和 _objc_autoreleasePoolPush() 釋放舊的池并創(chuàng)建新池窗慎;Exit(即將退出Loop) 時(shí)調(diào)用 _objc_autoreleasePoolPop() 來(lái)釋放自動(dòng)釋放池物喷。這個(gè) Observer 的 order 是 2147483647,優(yōu)先級(jí)最低遮斥,保證其釋放池子發(fā)生在其他所有回調(diào)之后峦失。
在主線程執(zhí)行的代碼,通常是寫在諸如事件回調(diào)术吗、Timer回調(diào)內(nèi)的尉辑。這些回調(diào)會(huì)被 RunLoop 創(chuàng)建好的 AutoreleasePool 環(huán)繞著,所以不會(huì)出現(xiàn)內(nèi)存泄漏较屿,開發(fā)者也不必顯示創(chuàng)建 Pool 了隧魄。
事件響應(yīng)
蘋果注冊(cè)了一個(gè) Source1 (基于 mach port 的) 用來(lái)接收系統(tǒng)事件,其回調(diào)函數(shù)為 __IOHIDEventSystemClientQueueCallback()。
當(dāng)一個(gè)硬件事件(觸摸/鎖屏/搖晃等)發(fā)生后,首先由 IOKit.framework 生成一個(gè) IOHIDEvent 事件并由 SpringBoard 接收漱贱。這個(gè)過(guò)程的詳細(xì)情況可以參考這里憔辫。SpringBoard 只接收按鍵(鎖屏/靜音等),觸摸胯努,加速,接近傳感器等幾種 Event,隨后用 mach port 轉(zhuǎn)發(fā)給需要的App進(jìn)程辉川。隨后蘋果注冊(cè)的那個(gè) Source1 就會(huì)觸發(fā)回調(diào),并調(diào)用 _UIApplicationHandleEventQueue() 進(jìn)行應(yīng)用內(nèi)部的分發(fā)拴测。
_UIApplicationHandleEventQueue() 會(huì)把 IOHIDEvent 處理并包裝成 UIEvent 進(jìn)行處理或分發(fā)乓旗,其中包括識(shí)別 UIGesture/處理屏幕旋轉(zhuǎn)/發(fā)送給 UIWindow 等。通常事件比如 UIButton 點(diǎn)擊集索、touchesBegin/Move/End/Cancel 事件都是在這個(gè)回調(diào)中完成的屿愚。

手勢(shì)識(shí)別
當(dāng)上面的 _UIApplicationHandleEventQueue() 識(shí)別了一個(gè)手勢(shì)時(shí),其首先會(huì)調(diào)用 Cancel 將當(dāng)前的 touchesBegin/Move/End 系列回調(diào)打斷务荆。隨后系統(tǒng)將對(duì)應(yīng)的 UIGestureRecognizer 標(biāo)記為待處理妆距。
蘋果注冊(cè)了一個(gè) Observer 監(jiān)測(cè) BeforeWaiting (Loop即將進(jìn)入休眠) 事件,這個(gè)Observer的回調(diào)函數(shù)是 _UIGestureRecognizerUpdateObserver()函匕,其內(nèi)部會(huì)獲取所有剛被標(biāo)記為待處理的 GestureRecognizer娱据,并執(zhí)行GestureRecognizer的回調(diào)。
當(dāng)有 UIGestureRecognizer 的變化(創(chuàng)建/銷毀/狀態(tài)改變)時(shí)盅惜,這個(gè)回調(diào)都會(huì)進(jìn)行相應(yīng)處理中剩。
界面更新
當(dāng)在操作 UI 時(shí)忌穿,比如改變了 Frame、更新了 UIView/CALayer 的層次時(shí)结啼,或者手動(dòng)調(diào)用了 UIView/CALayer 的 setNeedsLayout/setNeedsDisplay方法后掠剑,這個(gè) UIView/CALayer 就被標(biāo)記為待處理,并被提交到一個(gè)全局的容器去郊愧。
蘋果注冊(cè)了一個(gè) Observer 監(jiān)聽(tīng) BeforeWaiting(即將進(jìn)入休眠) 和 Exit (即將退出Loop) 事件澡腾,回調(diào)去執(zhí)行一個(gè)很長(zhǎng)的函數(shù):
_ZN2CA11Transaction17observer_callbackEP19__CFRunLoopObservermPv()。這個(gè)函數(shù)里會(huì)遍歷所有待處理的 UIView/CAlayer 以執(zhí)行實(shí)際的繪制和調(diào)整糕珊,并更新 UI 界面动分。

定時(shí)器
NSTimer 其實(shí)就是 CFRunLoopTimerRef,他們之間是 toll-free bridged 的红选。一個(gè) NSTimer 注冊(cè)到 RunLoop 后澜公,RunLoop 會(huì)為其重復(fù)的時(shí)間點(diǎn)注冊(cè)好事件。例如 10:00, 10:10, 10:20 這幾個(gè)時(shí)間點(diǎn)喇肋。RunLoop為了節(jié)省資源坟乾,并不會(huì)在非常準(zhǔn)確的時(shí)間點(diǎn)回調(diào)這個(gè)Timer。Timer 有個(gè)屬性叫做 Tolerance (寬容度)蝶防,標(biāo)示了當(dāng)時(shí)間點(diǎn)到后甚侣,容許有多少最大誤差。
如果某個(gè)時(shí)間點(diǎn)被錯(cuò)過(guò)了间学,例如執(zhí)行了一個(gè)很長(zhǎng)的任務(wù)殷费,則那個(gè)時(shí)間點(diǎn)的回調(diào)也會(huì)跳過(guò)去,不會(huì)延后執(zhí)行低葫。就比如等公交详羡,如果 10:10 時(shí)我忙著玩手機(jī)錯(cuò)過(guò)了那個(gè)點(diǎn)的公交,那我只能等 10:20 這一趟了嘿悬。
CADisplayLink 是一個(gè)和屏幕刷新率一致的定時(shí)器(但實(shí)際實(shí)現(xiàn)原理更復(fù)雜实柠,和 NSTimer 并不一樣,其內(nèi)部實(shí)際是操作了一個(gè) Source)善涨。如果在兩次屏幕刷新之間執(zhí)行了一個(gè)長(zhǎng)任務(wù)窒盐,那其中就會(huì)有一幀被跳過(guò)去(和 NSTimer 相似),造成界面卡頓的感覺(jué)钢拧。在快速滑動(dòng)TableView時(shí)蟹漓,即使一幀的卡頓也會(huì)讓用戶有所察覺(jué)。
PerformSelecter
當(dāng)調(diào)用 NSObject 的 performSelecter:afterDelay: 后娶靡,實(shí)際上其內(nèi)部會(huì)創(chuàng)建一個(gè) Timer 并添加到當(dāng)前線程的 RunLoop 中牧牢。所以如果當(dāng)前線程沒(méi)有 RunLoop,則這個(gè)方法會(huì)失效姿锭。
當(dāng)調(diào)用 performSelector:onThread: 時(shí)塔鳍,實(shí)際上其會(huì)創(chuàng)建一個(gè) Timer 加到對(duì)應(yīng)的線程去,同樣的呻此,如果對(duì)應(yīng)線程沒(méi)有 RunLoop 該方法也會(huì)失效轮纫。
關(guān)于GCD
實(shí)際上 RunLoop 底層也會(huì)用到 GCD 的東西,比如 RunLoop 是用 dispatch_source_t 實(shí)現(xiàn)的 Timer焚鲜。但同時(shí) GCD 提供的某些接口也用到了 RunLoop掌唾, 例如 dispatch_async()。
當(dāng)調(diào)用 dispatch_async(dispatch_get_main_queue(), block) 時(shí)忿磅,libDispatch 會(huì)向主線程的 RunLoop 發(fā)送消息糯彬,RunLoop會(huì)被喚醒,并從消息中取得這個(gè) block葱她,并在回調(diào) CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE() 里執(zhí)行這個(gè) block撩扒。但這個(gè)邏輯僅限于 dispatch 到主線程,dispatch 到其他線程仍然是由 libDispatch 處理的吨些。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末搓谆,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子豪墅,更是在濱河造成了極大的恐慌泉手,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,692評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件偶器,死亡現(xiàn)場(chǎng)離奇詭異斩萌,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)屏轰,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,482評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門术裸,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人亭枷,你說(shuō)我怎么就攤上這事袭艺。” “怎么了叨粘?”我有些...
    開封第一講書人閱讀 162,995評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵猾编,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我升敲,道長(zhǎng)答倡,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,223評(píng)論 1 292
  • 正文 為了忘掉前任驴党,我火速辦了婚禮瘪撇,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己倔既,他們只是感情好恕曲,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,245評(píng)論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著渤涌,像睡著了一般佩谣。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上实蓬,一...
    開封第一講書人閱讀 51,208評(píng)論 1 299
  • 那天茸俭,我揣著相機(jī)與錄音,去河邊找鬼安皱。 笑死调鬓,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的酌伊。 我是一名探鬼主播腾窝,決...
    沈念sama閱讀 40,091評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼腺晾!你這毒婦竟也來(lái)了燕锥?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,929評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤悯蝉,失蹤者是張志新(化名)和其女友劉穎归形,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體鼻由,經(jīng)...
    沈念sama閱讀 45,346評(píng)論 1 311
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡暇榴,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,570評(píng)論 2 333
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了蕉世。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片蔼紧。...
    茶點(diǎn)故事閱讀 39,739評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖狠轻,靈堂內(nèi)的尸體忽然破棺而出奸例,到底是詐尸還是另有隱情,我是刑警寧澤向楼,帶...
    沈念sama閱讀 35,437評(píng)論 5 344
  • 正文 年R本政府宣布查吊,位于F島的核電站,受9級(jí)特大地震影響湖蜕,放射性物質(zhì)發(fā)生泄漏逻卖。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,037評(píng)論 3 326
  • 文/蒙蒙 一昭抒、第九天 我趴在偏房一處隱蔽的房頂上張望评也。 院中可真熱鬧炼杖,春花似錦、人聲如沸盗迟。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,677評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)诈乒。三九已至罩扇,卻和暖如春婆芦,著一層夾襖步出監(jiān)牢的瞬間怕磨,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,833評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工消约, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留肠鲫,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,760評(píng)論 2 369
  • 正文 我出身青樓或粮,卻偏偏與公主長(zhǎng)得像导饲,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子氯材,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,647評(píng)論 2 354

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

  • 一渣锦、什么是RunLoop 基本作用: 保持程序的持續(xù)運(yùn)行; 處理App中的各種事件(比如觸摸事件氢哮、定時(shí)器事件袋毙、Se...
    magic_pill閱讀 911評(píng)論 0 0
  • 本文首發(fā)于我的個(gè)人博客:「程序員充電站」[https://itcharge.cn]文章鏈接:「?jìng)魉烷T」[https...
    ITCharge閱讀 60,159評(píng)論 50 539
  • 原文地址:http://blog.ibireme.com/2015/05/18/runloop/ RunLoop ...
    大餅炒雞蛋閱讀 1,156評(píng)論 0 6
  • 轉(zhuǎn)載:http://www.cocoachina.com/ios/20150601/11970.html RunL...
    Gatling閱讀 1,438評(píng)論 0 13
  • 早上早早起床在樓下做早餐听盖,倆個(gè)小家伙起床自己找了衣服穿,吃飯時(shí)我看見(jiàn)他倆穿的衣服太少裂七,就讓加件衣服皆看,在我勸說(shuō)下婉婷...
    言和婉閱讀 113評(píng)論 0 0