幾個iOS基礎題目總結

前言:
最近看到大佬匯集的iOS面試題,個人感覺還不錯,打算試著探索一下這些問題的答案,也鞏固一下我自己基礎知識。這篇文章先總結一下基礎知識的答案吧。其中有些錯誤或不全的地方望指教。

-----------------------------------------持續更新中---------------------------------

iOS 基礎題

1:分類和擴展有什么區別?可以分別用來做什么?分類有哪些局限性?分類的結構體里面有哪些成員?

分類和擴展的作用

1:category的主要作用是為已經存在的類添加方法
下面也有其他作用可以了解下:
2:可以把類的實現分開在幾個不同的文件里面,
(可以減少單個文件的體積
可以把不同的功能組織到不同的category里
可以由多個開發者共同完成一個類
可以按需加載想要的category)
3:模擬多繼承
4:把framework的私有方法公開
擴展的作用:為一個類添加額外的原來沒有變量,方法和屬性

類別與類擴展的區別

1:extension在編譯期決定,它就是類的一部分,
在編譯期和頭文件里的@interface以及實現文件里的@implement一起形成一個完整的類,
它伴隨類的產生而產生,亦隨之一起消亡。
extension一般用來隱藏類的私有信息,
你必須有一個類的源碼才能為一個類添加extension,所以你無法為系統的類比如NSString添加extension
但是category則完全不一樣,它是在運行時候決定的.
類擴展是在編譯階段被添加到類中,而類別是在運行時添加到類中。
extension可以添加實例變量,而category是無法添加實例變量的
2:類擴展中聲明的方法沒被實現,編譯器會報警,但是類別中的方法沒被實現編譯器是不會有任何警告的。

分類局限性

(1)無法向類中添加新的實例變量。
(2)名稱沖突,即當類別中的方法與原始類方法名稱沖突時,類別具有更高的優先級。
(3)如果多個分類中都有和原有類中同名的方法, 那么調用該方法的時候執行誰由編譯器決定;編譯器會執行最后一個參與編譯的分類中的方法

在runtime層,category用結構體category_t

typedef struct category_t {
    const char *name;
    classref_t cls;
    struct method_list_t *instanceMethods;
    struct method_list_t *classMethods;
    struct protocol_list_t *protocols;
    struct property_list_t *instanceProperties;
} category_t;

從源碼中我們可以看出分類結構體成員:

1)類的名字(name)
2)類(cls)
3)category中所有給類添加的實例方法的列表(instanceMethods)
4)category中所有添加的類方法的列表(classMethods)
5)category實現的所有協議的列表(protocols)
6)category中添加的所有屬性(instanceProperties)

參考鏈接:
分類和擴展說明參考
美團關于分類的源碼解析說明
官方分類源碼地址

2:atomic的實現機制;為什么不能保證絕對的線程安全

這個問題我覺得看這個就夠了stackoverflow關于atomic和nonatomic的一個問題
當然也可以看別人根據stackoverflow這個問題總結好的中文說明

簡單來說:atomic 會加一個鎖來保障線程安全,也就是保證了讀寫操作是安全的,并且引用計數會 +1,來向調用者保證這個對象會一直存在.
但是不能保證線程安全,比如當線程A setter操作時,這時B線程的setter操作會等待。當A線程的setter結束后,B線程進行setter操作,
然后當A線程需要getter操作時,卻有可能獲得了在B線程中的值,這就破壞了線程安全

3:哪些場景可以觸發離屏渲染?

首先我們要知道什么是離屏渲染:
離屏渲染Off-Screen Rendering 指的是GPU在當前屏幕緩沖區以外新開辟一個緩沖區進行渲染操作。
離屏渲染會先在屏幕外創建新緩沖區,離屏渲染結束后,再從離屏切到當前屏幕
還有另外一種屏幕渲染方式-當前屏幕渲染On-Screen Rendering ,
指的是GPU的渲染操作是在當前用于顯示的屏幕緩沖區中進行。
以下方式會觸發離屏幕渲染
1:使用系統提供的圓角效果也會觸發離屏渲染.(masksToBounds = true&&cornerRadius>0才會引發離屏渲染)
2:重寫drawRect
3:layer.shadow(Shawdow 可以通過指定路徑來取消離屏渲染)
4:layer.mask(Mask 效果無法取消離屏渲染,使用混合圖層的方法來模擬 mask 效果,性能各方面都是和無效果持平。)
5:layer.allowsGroupOpacity(GroupOpacity 是指 CALayer 的allowsGroupOpacity屬性,UIView 的alpha屬性等同于 CALayer opacity屬性,
開啟離屏渲染的條件是:layer.opacity != 1.0并且有子 layer 或者背景圖。

layer.allowsEdgeAntialiasing(該屬性用于消除鋸齒,離屏渲染條件旋轉視圖并且設置layer.allowsEdgeAntialiasing = true)
6:layer.shouldRasterize(光柵化會觸發離屏渲染,開啟 Rasterization=true 后,GPU 只合成一次內容,然后復用合成的結果;合成的內容超過 100ms 沒有使用會從緩存里移除,在更新內容時還會產生更多的離屏渲染。對于內容不發生變化的視圖,原本拖后腿的離屏渲染就成為了助力)

參考:
離屏渲染優化詳解
Instruments性能優化-Core Animation
繪制像素到屏幕上
界面流暢性優化

4:被weak修飾的對象在被釋放的時候會發生什么?是如何實現的?知道sideTable么?里面的結構可以畫出來么?

釋放時,調用clearDeallocating函數。clearDeallocating函數首先根據對象地址獲取所有weak指針地址的數組,然后遍歷這個數組把其中的數據設為nil,最后把這個entry從weak表中刪除,最后清理對象的記錄.
objc_clear_deallocating該函數的動作如下:
1、從weak表中獲取廢棄對象的地址為鍵值的記錄
2、將包含在記錄中的所有附有 weak修飾符變量的地址,賦值為nil
3、將weak表中該記錄刪除
4、從引用計數表中刪除廢棄對象的地址為鍵值的記錄
SideTable 這個結構體主要用于管理對象的引用計數和 weak 表。在 NSObject.mm 中聲明其數據結構:

struct SideTable {
    spinlock_t slock;//保證原子操作的自旋鎖
    RefcountMap refcnts;//引用計數的 hash 表
    weak_table_t weak_table;//weak 引用全局 hash 表

    SideTable() {
        memset(&weak_table, 0, sizeof(weak_table));
    }

    ~SideTable() {
        _objc_fatal("Do not delete SideTable.");
    }

    void lock() { slock.lock(); }
    void unlock() { slock.unlock(); }
    void forceReset() { slock.forceReset(); }

    // Address-ordered lock discipline for a pair of side tables.

    template<HaveOld, HaveNew>
    static void lockTwo(SideTable *lock1, SideTable *lock2);
    template<HaveOld, HaveNew>
    static void unlockTwo(SideTable *lock1, SideTable *lock2);
};

參考:
objc-weak.mm源碼
weak 弱引用的實現方式
iOS 底層解析weak的實現原理

5:KVO的底層實現?如何取消系統默認的KVO并手動觸發(給KVO的觸發設定條件:改變的值符合某個條件時再觸發KVO)?

當你觀察一個對象時,一個新的類會被動態創建。這個類繼承自該對象的原本的類,并重寫了被觀察屬性的 setter 方法。重寫的 setter 方法會負責在調用原 setter 方法之前和之后,通知所有觀察對象:值的更改。最后通過 isa 混寫(isa-swizzling) 把這個對象的 isa 指針 ( isa 指針告訴 Runtime 系統這個對象的類是什么 ) 指向這個新創建的子類,
對象就神奇的變成了新創建的子類的實例

關閉默認的KVO重寫方法

+(BOOL)automaticallyNotifiesObserversForKey:(NSString *)key {
    return NO;
}//如果返回NO,KVO無法自動運作,需手動觸發

鍵值觀察通知依賴于 NSObject 的兩個方法: willChangeValueForKey: 和 didChangevlueForKey
在一個被觀察屬性發生改變之前, willChangeValueForKey: 一定會被調用,這就 會記錄舊的值。而當改變發生后,
observeValueForKey:ofObject:change:context: 會被調用,
并且 didChangeValueForKey: 也會被調用。如果可以手動實現這些調用,就可以實現手動觸發.

參考:
如何自己動手實現 KVO
apple用什么方式實現對一個對象的KVO

6:一個int變量被__block修飾與否的區別?

Block不允許修改外部變量的值,這里所說的外部變量的值,指的是棧中指針的內存地址。
__block 所起到的作用就是只要觀察到該變量被 block 所持有。
__block 后,實際上成為了一個結構體,block內截獲了 該結構體的指針。
在block中使用自動變量時,使用的是 指針指向的結構體中的 自動變量。
ARC環境下,會被copy到堆上。(ARC環境下,一旦Block賦值就會觸發copy,__block就會copy到堆上,Block也是__NSMallocBlock。
ARC環境下也是存在__NSStackBlock的時候,這種情況下,__block就在棧上。)
MRC環境下,只有copy,__block才會被復制到堆上,否則,__block一直都在棧上。

測試,其實最好的方法是動手測試,這邊我只測試了ARC環境下的。我在.main.m的測試代碼如下:

  __block int a1 = 1;
        int a2 = 1;
        NSLog(@"__block定義前a1:%p", &a1);
        NSLog(@"__block定義前a2:%p", &a2);;
        void (^foo)(void) = ^{
            a1 = 2;
        
            NSLog(@"block內部a1:%p", &a1);
            NSLog(@"block內部a2:%p", &a2);
        };
        NSLog(@"重新定義后a1:%p", &a1);
        NSLog(@"重新定義后a2:%p", &a2);
        NSLog(@"foo =%@",foo);
        foo();
    ——---------------------- 輸出結果如下:-------------------------------    
 
        __block定義前a1:0x7fff53814128 
        __block定義前a2:0x7fff5381410c  
        重新定義后a1:0x60400003dd98
        重新定義后a2:0x7fff5381410c
        foo =<__NSMallocBlock__: 0x60c000244830>
        block內部a1:0x60400003dd98
        block內部a2:0x60400025dbd8

通知打印結果可以發現a1,a2blcok內部和定義前的地址字節數相差很大,堆地址要小于棧地址,又因為iOS中一個進程的棧區內存只有1M,Mac也只有8M,所以a1和a2在block內部都會被copy到堆上,只不過一個值的copy,一個是地址copy。

然后clang -rewrite-objc main.m查看一下源碼,如果clang -rewrite-objc報錯,可以像我一樣嘗試
xcrun -sdk iphonesimulator11.0 clang -rewrite-objc main.m

源碼如下:

//加上__block 后,實際上成為了一個結構體,block內截獲了 該結構體的指針
struct __Block_byref_a1_0 {
  void *__isa;
__Block_byref_a1_0 *__forwarding;
 int __flags;
 int __size;
 int a1;
};

struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  int a2;
  ////截獲的結構體指針
  __Block_byref_a1_0 *a1; // by ref
  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int _a2, __Block_byref_a1_0 *_a1, int flags=0) : a2(_a2), a1(_a1->__forwarding) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
  //指針引用
  __Block_byref_a1_0 *a1 = __cself->a1; // bound by ref
  //a2只是單純的值拷貝,。Block僅僅捕獲了a2的值,并沒有捕獲a2的內存地址。
  int a2 = __cself->a2; // bound by copy

            (a1->__forwarding->a1) = 2;

            NSLog((NSString *)&__NSConstantStringImpl__var_folders_dd_4kldckw11bv3zn6tgktzys440000gn_T_main_5a4382_mi_2, &(a1->__forwarding->a1));
            NSLog((NSString *)&__NSConstantStringImpl__var_folders_dd_4kldckw11bv3zn6tgktzys440000gn_T_main_5a4382_mi_3, &a2);
        }
static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {_Block_object_assign((void*)&dst->a1, (void*)src->a1, 8/*BLOCK_FIELD_IS_BYREF*/);}

static void __main_block_dispose_0(struct __main_block_impl_0*src) {_Block_object_dispose((void*)src->a1, 8/*BLOCK_FIELD_IS_BYREF*/);}

從源碼中可以看出:

帶有 __block的變量也被轉化成了一個結構體__Block_byref_i_0,很清楚看到了__block的引用過程。
而Block僅僅捕獲了a2的值,并沒有捕獲a2的內存地址。所以在__main_block_func_0這個函數中即使我們重寫這個自動變量a2的值,
也無法改變Block外面自動變量a2的值

參考:
iOS中__block 關鍵字的底層實現原理
深入研究Block捕獲外部變量和__block實現原理

7:為什么在block外部使用__weak修飾的同時需要在內部使用__strong修飾

_weak是為了解決循環引用問題,(如果block和對象相互持有就會形成循環引用)
而__strong在Block內部修飾的對象,會保證,在使用這個對象在block內,
這個對象都不會被釋放,strongSelf僅僅是個局部變量,存在棧中,會在block執行結束后回收,不會再造成循環引用。
__strong主要是用在多線程中,防止對象被提前釋放。

參考:
iOS __weak和__strong在Block中的使用

題外話:

有時候我們經常也會被問到block為什么 常使用copy關鍵字?

官方中有如下一段話:

block 應該用copy

總結別人的話來說:

block 使用 copy 是從 MRC遺留下來的“傳統”,在 MRC 中,方法內部的 block 是在棧區的,使用 copy 可以把它放到堆區.在 ARC 中寫不寫都行:對于 block 使用 copy 還是 strong 效果是一樣的,但寫上 copy 也無傷大雅,還能時刻提醒我們:編譯器自動對 block 進行了 copy 操作。
如果不寫 copy ,該類的調用者有可能會忘記或者根本不知道“編譯器會自動對 block 進行了 copy 操作”

8:講一下對象,類對象,元類,跟元類結構體的組成以及他們是如何相關聯的?為什么對象方法沒有保存的對象結構體里,而是保存在類對象的結構體里.

對象isa指向類對象,類對象的isa指向元類。元類isa指向根元類。
根元類的isa指針指向自己,superclass指針指向NSObject類
實例對象結構體只有一個isa變量,指向實例對象所屬的類。
類對象有isa,superclass,方法,屬性,協議列表,以及成員變量的
描述。
所有的對象調用方法都是一樣的,沒有必要存在對象中,對象可以有
無數個,類對象就有一個所以只需存放在類對象中

可以從官方objc.h源碼里面找到實例定義


/// Represents an instance of a class.
struct objc_object {
    Class _Nonnull isa  OBJC_ISA_AVAILABILITY;
};

可以在runtime.h里面找到類對象的定義

struct objc_class {
    Class _Nonnull isa  OBJC_ISA_AVAILABILITY;

#if !__OBJC2__
    //向該類所繼承的父類對象 
    Class _Nullable super_class                              OBJC2_UNAVAILABLE;
    const char * _Nonnull name                               OBJC2_UNAVAILABLE;
    long version                                             OBJC2_UNAVAILABLE;
    long info                                                OBJC2_UNAVAILABLE;
    long instance_size                                       OBJC2_UNAVAILABLE;
    //成員變量列表   
    struct objc_ivar_list * _Nullable ivars                  OBJC2_UNAVAILABLE;
    //方法列表
    struct objc_method_list * _Nullable * _Nullable methodLists                    OBJC2_UNAVAILABLE;//方法列表
    //用于緩存調用過的方法    
    struct objc_cache * _Nonnull cache                       OBJC2_UNAVAILABLE;
    //協議鏈表用來存儲聲明遵守的正式協議
    struct objc_protocol_list * _Nullable protocols          OBJC2_UNAVAILABLE;
#endif

}

參考:
iOS開發·runtime原理與實踐: 基本知識篇
一個objc對象如何進行內存布局

9:iOS 中內省的幾個方法?class方法和objc_getClass方法有什么區別?

題外話:原諒我看了這道面試題,第一次聽說內省,才疏學淺,太菜了,只能好好搜索學習了一番。

內省是對象揭示自己作為一個運行時對象的詳細信息的一種能力。包括對象在繼承樹上的位置,對象是否遵循特定的協議,以及是否可以響應特定的消息。NSObject協議和類定義了很多內省方法,用于查詢運行時信息,以便根據對象的特征進行識別。
isKindOfClass:Class
檢查對象是否是那個類或者其繼承類實例化的對象
isMemberOfClass:Class
檢查對象是否是那個類但不包括繼承類而實例化的對象
respondToSelector:selector
檢查對象是否包含這個方法
conformsToProtocol:protocol
檢查對象是否符合協議,是否實現了協議中所有的必選方法。
object_getClass(obj)返回的是obj中的isa指針;
而[obj class]則分兩種情況:
一:當obj為實例對象時,
[obj class]中class是實例方法:- (Class)class,
返回的obj對象中的isa指針,返回的是類對象;
二:當obj為類對象(包括元類和根類以及根元類)時,調用的是類方法:+ (Class)class,返回的結果為其本身

可以在ViewController通過簡單代碼驗證一下

//currentClass現在是類對象   
Class currentClass = [self class];
     //都指向實例對象isa指定的類對象
     NSLog(@"currentClass = %p getClass=%p",currentClass ,object_getClass(self));
     //class指向類對象本身  getClass指向類對象isa指向元類
     NSLog(@"currentClass = %p  getClass=%p",[currentClass class],object_getClass(currentClass));
      const char *getClassName = object_getClassName(currentClass);
     //實例對象指向類,類執行元類,元類指向根元類,根元類指向自己
     for (int i = 1; i < 5; i++) {
         NSLog(@"Following the isa pointer %d times gives %p %@---%s", i, currentClass,currentClass,getClassName);
         currentClass = object_getClass(currentClass);
         getClassName = object_getClassName(currentClass);
     }

輸出結果如下:

currentClass = 0x10ab29198 getClass=0x10ab29198
currentClass = 0x10ab29198  getClass=0x10ab291c0
Following the isa pointer 1 times gives 0x10ab29198 ViewController---ViewController
Following the isa pointer 2 times gives 0x10ab291c0 ViewController---NSObject
Following the isa pointer 3 times gives 0x10b819e58 NSObject---NSObject
Following the isa pointer 4 times gives 0x10b819e58 NSObject---NSObject

參考
Objective-C的內省(Introspection)小結

10:RunLoop的作用是什么?它的內部工作機制了解么?(最好結合線程和內存管理來說)

這一塊平時用的比較少,了解不是很多。其有時間真的好好靜下心來看一下相關東西了。

字面意思是“消息循環、運行循環”,runloop內部實際上就是一個do-while循環,它在循環監聽著各種事件源、消息,對他們進行管理并分發給線程來執行。
線程和 RunLoop 之間是一一對應的。
運行機制從官方文檔說明
翻譯過來如下:
1.通知觀察者將要進入運行循環。
2.通知觀察者將要處理計時器。
3.通知觀察者任何非基于端口的輸入源即將觸發。
4.觸發任何準備觸發的基于非端口的輸入源。
5.如果基于端口的輸入源準備就緒并等待觸發,請立即處理該事件。轉到第9步。
6.通知觀察者線程即將睡眠。
7.將線程置于睡眠狀態,直到發生以下事件之一:

  • 事件到達基于端口的輸入源。
  • 計時器運行。
  • 為運行循環設置的超時值到期。
  • 運行循環被明確喚醒。

8.通知觀察者線程被喚醒。
9.處理待處理事件。

  • 如果觸發了用戶定義的計時器,則處理計時器事件并重新啟動循環。轉到第2步。
  • 如果輸入源被觸發,則傳遞事件。
  • 如果運行循環被明確喚醒但尚未超時,請重新啟動循環。轉到第2步。

10.通知觀察者運行循環已退出。
這里借用一下這里的圖片

RunLoop_1.png

參考

深入理解RunLoop
關于Runloop的原理探究及基本使用

11:談談消息轉發機制實現

先會調用objc_msgSend方法,首先在Class中的緩存查找IMP,沒有緩存則初始化緩存。如果沒有找到,則向父類的Class查找。如果一直查找到根類仍舊沒有實現,則執行消息轉發。
1、調用resolveInstanceMethod:方法。允許用戶在此時為該Class動態添加實現。如果有實現了,則調用并返回YES,重新開始objc_msgSend流程。這次對象會響應這個選擇器,一般是因為它已經調用過了class_addMethod。如果仍沒有實現,繼續下面的動作。
2、調用forwardingTargetForSelector:方法,嘗試找到一個能響應該消息的對象。如果獲取到,則直接把消息轉發給它,返回非nil對象。否則返回nil,繼續下面的動作。注意這里不要返回self,否則會形成死循環。
3、調用methodSignatureForSelector:方法,嘗試獲得一個方法簽名。如果獲取不到,則直接調用doesNotRecognizeSelector拋出異常。如果能獲取,則返回非nil;傳給一個NSInvocation并傳給forwardInvocation:
4、調用forwardInvocation:方法,將第三步獲取到的方法簽名包裝成Invocation傳入,如何處理就在這里面了,并返回非nil。
5、調用doesNotRecognizeSelector:,默認的實現是拋出異常。如果第三步沒能獲得一個方法簽名,執行該步驟 。

參考:
Objective-C 消息發送與轉發機制原理
深入淺出理解消息的傳遞和轉發機制
-----------------------------------------未完待續-----------------------------------

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,646評論 6 533
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,595評論 3 418
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,560評論 0 376
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,035評論 1 314
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,814評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,224評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,301評論 3 442
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,444評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,988評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,804評論 3 355
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,998評論 1 370
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,544評論 5 360
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,237評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,665評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,927評論 1 287
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,706評論 3 393
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,993評論 2 374

推薦閱讀更多精彩內容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,665評論 25 708
  • 1、通過CocoaPods安裝項目名稱項目信息 AFNetworking網絡請求組件 FMDB本地數據庫組件 SD...
    陽明先生_X自主閱讀 16,000評論 3 119
  • 蘇曉妍低著頭緊咬著嘴唇,然后釋然地嘆了口氣,仿佛做了很大決定之后面朝著我,眼神中充滿著希冀,說不出那是一種什么樣的...
    丿深海不藍閱讀 574評論 1 4