iOS-底層原理-類&類結(jié)構(gòu)分析 isa--superclass--bits

本文的主要目的是分析 類 & 類的結(jié)構(gòu),整篇都是圍繞一個展開的一些探索

1.類的分析之Class isa和Class superclass ---> isa走向和繼承關(guān)系鏈

類的分析 主要是分析 isa的走向 以及 類之間的繼承關(guān)系,注意對象之間沒有繼承關(guān)系

分析類之前提出幾個問題
1.Class到底是什么?Class之間的關(guān)系?
2.對象isaisa之間的區(qū)別?分別存儲的是什么值?
3.對象方法存儲在中,那類方法存儲在哪里呢?如果一直有相應(yīng)的來存儲那么盡頭在哪里呢?
4.isasupperClass是什么關(guān)系? isa到底有哪些作用?
5.在內(nèi)存中到底存在幾份?

類的底層定義,runtime.h里早期類的定義,現(xiàn)在已經(jīng)棄用

struct objc_class {
    Class _Nonnull isa  OBJC_ISA_AVAILABILITY; // isa

#if !__OBJC2__
    Class _Nullable super_class                              OBJC2_UNAVAILABLE; // super_class 父類指針
    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;//協(xié)議列表
#endif

} OBJC2_UNAVAILABLE;
/* Use `Class` instead of `struct objc_class *` */

objc_object的部分代碼

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

public:

    // ISA() assumes this is NOT a tagged pointer object
    Class ISA(bool authenticated = false);

    // rawISA() assumes this is NOT a tagged pointer object or a non pointer ISA
    Class rawISA();

    // getIsa() allows this to be a tagged pointer object
    Class getIsa();
    
    uintptr_t isaBits() const;


    bool hasNonpointerIsa();
    bool isTaggedPointer();
    bool isTaggedPointerOrNil();
    bool isBasicTaggedPointer();
    bool isExtTaggedPointer();
    bool isClass();

    // object may have associated objects?
    bool hasAssociatedObjects();
    void setHasAssociatedObjects();

    // object may be weakly referenced?
    bool isWeaklyReferenced();
    void setWeaklyReferenced_nolock();

    // object may have -.cxx_destruct implementation?
    bool hasCxxDtor();

    // Optimized calls to retain/release methods
    id retain();
    void release();
    id autorelease();

    // Implementations of retain/release methods
    id rootRetain();
    bool rootRelease();
    id rootAutorelease();
    bool rootTryRetain();
    bool rootReleaseShouldDealloc();
    uintptr_t rootRetainCount();

    // Implementation of dealloc methods
    bool rootIsDeallocating();
    void clearDeallocating();
    void rootDealloc();
};

類的新的定義,部分代碼, objc-runtime-new.h文件里面

struct objc_class : objc_object {
    // Class ISA; // isa 繼承自objc_object
    Class superclass; //父類指針
    cache_t cache;             // formerly cache pointer and vtable //緩存
    class_data_bits_t bits;    // class_rw_t * plus custom rr/alloc flags // 存儲類信息(協(xié)議、方法、成員變量、屬性)
    class_rw_t *data() const {
        return bits.data();
    }
    void setData(class_rw_t *newData) {
        bits.setData(newData);
    }
}

Class id宏定義

typedef struct objc_class *Class;
typedef struct objc_object *id;

總結(jié):
1.Classobjc_class,來自于底層objc_class結(jié)構(gòu)體
2.idobjc_object對象,即對象來自于objc_object結(jié)構(gòu)體
3.objc_class : objc_object,objc_class繼承自objc_object結(jié)構(gòu)體,objc_classisa繼承自objc_object,所以,和對象都有Class isa指針
4.superclass有繼承關(guān)系,對象沒有superclass沒有繼承關(guān)系
5.本篇博客研究類的 isa、superclass、bits三個成員變量,很有規(guī)律性,各個源碼版本略有差異

1.準備工作
main.m里自定義類CJLPerson,代碼如下

// 協(xié)議 protocol
@protocol CJLPersonDelegate <NSObject>
//實例方法
- (void)sendDataToController:(NSMutableArray *)dataArray;

// 類方法
+ (void)sendString:(NSString *)string;

// 可選實例方法
@optional
- (void)showKeyboard;

// 可選類方法
@optional
+ (void)closeKeyboard;

@end

@interface CJLPerson : NSObject <CJLPersonDelegate> {
    // 成員變量
    NSString * hobby;
    NSString * sex;
}
// 屬性
@property (nonatomic, copy) NSString * cjl_name;
@property (nonatomic, copy) NSString * number;

// 實例方法
- (void)sayHello:(NSString *)hello;
- (void)sayWorld;

// 類方法
+ (void)sayBye;
+ (void)sayLove;

@end

@implementation CJLPerson

- (void)sayHello:(NSString *)hello {

}

- (void)sayWorld {

}

+ (void)sayBye {

}

+ (void)sayLove {

}

- (void)sendDataToController:(NSMutableArray *)dataArray {
    
}

- (void)showKeyboard {
    
}

+ (void)sendString:(NSString *)string {
    
}

調(diào)用方式


int main(int argc, char * argv[]) {
    NSString * appDelegateClassName;
    @autoreleasepool {
        // Setup code that might create autoreleased objects goes here.
        appDelegateClassName = NSStringFromClass([AppDelegate class]);
        CJLPerson * person = [[CJLPerson alloc] init];
        NSLog(@"person == %@", person);
        
    }
    return UIApplicationMain(argc, argv, nil, appDelegateClassName);
}

@end

2.元類

首先,我們先通過一個案例的lldb調(diào)試先引入元類,對象isa指向isa指向的就是元類
通過斷點調(diào)試,lldb輸出如下,相信說明見注釋

我們由前面的內(nèi)容知道對象的內(nèi)存地址是第一個8字節(jié)是isa的值,其他后面的都是對應(yīng)屬性的值,假設(shè)都是NSString類型的屬性,都是8字節(jié),對象內(nèi)存示意圖如下

對象的內(nèi)存地址和內(nèi)存分布

{
    0x0100000104c01418 :Class isa 值 第一個8字節(jié)
    0x0100000104c01420 :name 值 第二個8字節(jié)
    0x0100000104c01428:height 值 第三個8字節(jié)
    0x0100000104c01430:weight 值 第四個8字節(jié)
    0x0100000104c01438:XXX   值  依次類推
}

類的源碼結(jié)構(gòu)

struct objc_class : objc_object {
    Class ISA; // Class 類型 8字節(jié) 繼承自objc_object, 第一8字節(jié)是isa
    Class superclass; // Class 類型 8字節(jié), 第二8字節(jié)是superclass也就是父類
    cache_t cache;             // 16字節(jié)
    class_data_bits_t bits;    // 
    ...
}

以上可知

1.對象的第一個8字節(jié)是isa
2.的第一個8字節(jié)是isa
3.的第二個8字節(jié)是supperclass,也就是父類
4.對象沒有supperclass,也就是說對象沒有繼承關(guān)系,只有繼承關(guān)系

(lldb) x/4gx person
0x600003705e40: 0x0100000104c014d9 0x0000000000000000 //0x0100000104c014d9 對象的isa
0x600003705e50: 0x0000000000000000 0x0000000000000000
(lldb) p/x 0x0100000104c014d9 & 0x0000000ffffffff8ULL // 對象的isa &上ISA_MASK
(unsigned long long) $1 = 0x0000000104c014d8 //類的16進制數(shù)值
(lldb) po 0x0000000104c014d8 //打印類的16進制數(shù)值為CJLPerson
CJLPerson

(lldb) p/x CJLPerson.class //p/x直接打印類的16進制數(shù)值,可以看到與上面的一致
(Class) $3 = 0x0000000104c014d8 CJLPerson

(lldb) x/4gx 0x0000000104c014d8 //打印類的4段內(nèi)存
0x104c014d8: 0x0000000104c014b0 0x00000001c28fc628 //0x0000000104c014b0 類的isa
0x104c014e8: 0x0001600003705e60 0x0001802100000000
(lldb) p/x 0x0000000104c014b0 & 0x0000000ffffffff8ULL // 類的isa &上ISA_MASK
(unsigned long long) $4 = 0x0000000104c014b0// 類的isa &上ISA_MASK的值與沒有&上ISA_MASK完全一致
// 說明類的isa 8字節(jié)64位中除了33位類信息之外其他全部為0

(lldb) po 0x0000000104c014b0 // 打印類的isa指向輸出結(jié)果依然是CJLPerson
CJLPerson

//0x0000000104c014d8 CJLPerson,名字相同,16進制數(shù)值不同,很明顯是兩個類,對象isa指向的是類,CJLPerson類本身
//0x0000000104c014b0 CJLPerson,名字相同,16進制數(shù)值不同,很明顯是兩個類,類isa指向的是元類,
//CJLPerson的元類,只是名字相同而已,暫且標記為CJLPerson_Metal
// CJLPerson和CJLPerson的元類(CJLPerson_Metal)是兩個不同的概念

(lldb) x/4gx 0x0000000104c014b0  //打印CJLPerson的元類的4段內(nèi)存
0x104c014b0: 0x00000001c28fc600 0x00000001c28fc600
0x104c014c0: 0x0003600002258080 0x0002e03500000000
(lldb) po 0x00000001c28fc600 //輸出CJLPerson_Metal的isa指向NSObject,此NSObject是NSObject的元類
NSObject   // 即NSObject_Metal與NSObject是兩個概念

(lldb) x/4gx 0x00000001c28fc600 // 繼續(xù)輸出NSObject_Metal的isa指向,依然指向自身
0x1c28fc600: 0x00000001c28fc600 0x00000001c28fc628
0x1c28fc610: 0x00036000022580c0 0x0003e03400000000
(lldb) x/4gx 0x00000001c28fc600 // 繼續(xù)輸出NSObject_Metal的isa指向,依然指向自身
0x1c28fc600: 0x00000001c28fc600 0x00000001c28fc628
0x1c28fc610: 0x00036000022580c0 0x0003e03400000000

(lldb) p/x NSObject.class    //打印NSObject.class類本身
(Class) $7 = 0x00000001c28fc628 NSObject
(lldb) 

//0x00000001c28fc628 NSObject
//0x00000001c28fc600 NSObject元類(NSObject_Metal)
// 這兩個是不同的概念,不同的類

define ISA_MASK 0x0000000ffffffff8ULL
根據(jù)調(diào)試過程,我們產(chǎn)生了一個疑問:為什么圖中的p/x 0x0100000104c014d9 & 0x0000000ffffffff8ULL與 p/x 0x0000000104c014b0 & 0x0000000ffffffff8ULL 中的類信息打印出來的類名都是CJLPerson,這兩個CJLPerson是什么關(guān)系呢?

0x0100000104c014d9person對象的isa指針地址,其&后得到的結(jié)果是 創(chuàng)建person的類CJLPerson
0x0000000104c014b0CJLPerson類的isa的所存儲的指針地址,即 CJLPerson類的類的地址,在Apple中,我們簡稱CJLPerson類的類為元類

0x0100000104c014d9 & 0x0000000ffffffff8ULL = 0x0000000104c014d8(或者是首地址不同) = CJLPerson,類名相同,這是CJLPerson類
0x0000000104c014b0 & 0x0000000ffffffff8ULL = 0x0000000104c014b0(或者是首地址不同) = CJLPerson,類名相同,這是CJLPerson元類,暫且標記為CJLPerson _metaClass

CJLPerson類CJLPerson _metaClass是兩個不同的,且每個在內(nèi)存中只有一份,且內(nèi)存地址不同,

元類是兩個不同的概念,只是體現(xiàn)出來的類名字相同而已

所以,兩個打印都是CJLPerson的根本原因就是因為元類導(dǎo)致的

3.元類的說明

下面來解釋什么是元類,主要有以下幾點說明:

我們都知道 對象isa 是指向,其實也是一個對象,可以稱為類對象,其isa指向蘋果定義的元類

元類系統(tǒng)給的,其定義創(chuàng)建都是由編譯器完成,在這個過程中,的歸屬來自于元類

元類 是類對象 的類,每個都有一個獨一無二的元類用來存儲 類方法的相關(guān)信息。

元類本身是沒有名稱的,由于與相關(guān)聯(lián),所以使用了同名一樣的名稱

下面通過lldb命令來探索元類的走向,也就是isa的走位,如下圖所示,可以得出一個關(guān)系鏈:

對象isa ---> 類isa ---> 元類isa ---> NSobject(根元類) ---> NSobject(根元類)

person(對象) ---> CJLPerson(類) ---> CJLPerson _metaClass(CJLPerson元類) ---> NSobject_metaClass (NSobject根元類)--->NSobject_metaClass(根元類自身)

CJLPerson不是CJLPerson_metaClass(CJLPerson元類),內(nèi)存地址不同,但是每一個只有一份
NSobject不是NSobject_metaClass(根元類),內(nèi)存地址不同,但是每一個只有一份

4.isa走位圖

isa走位圖.jpeg

5.LLDB調(diào)試 isa走位圖

Xcode截圖isa指向.jpeg

6.superclass指向

superclass走勢圖.jpeg

7.LLDB調(diào)試 superclass指向

Xcode截圖superclass指向.jpeg

8.蘋果官方的isa走位以及繼承關(guān)系圖

isa流程圖.png

9.LLDB調(diào)試 CJLTeacher ---> CJLPerson ---> NSObject 三級繼承關(guān)系isa走位及superclass指向

新增類CJLTeacher繼承于CJLPerson, CJLPerson繼承于NSObject,調(diào)用如下

@interface CJLTeacher : CJLPerson

@end

@implementation CJLTeacher

@end

int main(int argc, char * argv[]) {
    NSString * appDelegateClassName;
    @autoreleasepool {
        // Setup code that might create autoreleased objects goes here.
        appDelegateClassName = NSStringFromClass([AppDelegate class]);
        // ISA_MASK        0x0000000ffffffff8ULL
 
        CJLTeacher * teacher = [[CJLTeacher alloc] init];
        CJLPerson * person = [[CJLPerson alloc] init];
        NSObject * obj = [[NSObject alloc] init];
        NSLog(@"teacher == %@ person == %@ obj == %@", teacher, person, obj);
    }
    return UIApplicationMain(argc, argv, nil, appDelegateClassName);
}

LLDB調(diào)試,完美驗證iOS系統(tǒng)的isa走位及繼承關(guān)系圖

Teacher_Person_NSObject三級繼承關(guān)系isa和superclass.jpeg

10. clang 編譯位cpp文件 查看isa 和 superclass指向

CJLTeacher以及其元類isasuperclass指向

CJLTeacher以及元類isa和superclass指向.jpeg

CJLPerson以及其元類isasuperclass指向

CJLPerson以及元類isa和superclass指向.jpeg

NSObject根元類被編譯成OBJC_METACLASS_$_NSObject
CJLPerson元類被編譯成OBJC_METACLASS_$_CJLPerson
CJLTeacher元類被編譯成OBJC_METACLASS_$_CJLTeacher
NSObject被編譯成OBJC_CLASS_$_NSObject
CJLPerson被編譯成OBJC_CLASS_$_CJLPerson

static void OBJC_CLASS_SETUP_$_CJLTeacher(void ) {
    OBJC_METACLASS_$_CJLTeacher.isa = &OBJC_METACLASS_$_NSObject;
    OBJC_METACLASS_$_CJLTeacher.superclass = &OBJC_METACLASS_$_CJLPerson;
    OBJC_METACLASS_$_CJLTeacher.cache = &_objc_empty_cache;
    OBJC_CLASS_$_CJLTeacher.isa = &OBJC_METACLASS_$_CJLTeacher;
    OBJC_CLASS_$_CJLTeacher.superclass = &OBJC_CLASS_$_CJLPerson;
    OBJC_CLASS_$_CJLTeacher.cache = &_objc_empty_cache;
}
static void OBJC_CLASS_SETUP_$_CJLPerson(void ) {
    OBJC_METACLASS_$_CJLPerson.isa = &OBJC_METACLASS_$_NSObject;
    OBJC_METACLASS_$_CJLPerson.superclass = &OBJC_METACLASS_$_NSObject;
    OBJC_METACLASS_$_CJLPerson.cache = &_objc_empty_cache;
    OBJC_CLASS_$_CJLPerson.isa = &OBJC_METACLASS_$_CJLPerson;
    OBJC_CLASS_$_CJLPerson.superclass = &OBJC_CLASS_$_NSObject;
    OBJC_CLASS_$_CJLPerson.cache = &_objc_empty_cache;
}

2.類的分析之類信息存儲 ---> bits( class_data_bits_t 結(jié)構(gòu)體類 )

以上研究了類的isa走勢圖和superclass繼承關(guān)系圖,那么類的其他信息比如方法、協(xié)議、屬性、成員變量存在什么地方呢?

cache_t cache 是方法緩存
很明顯類的相關(guān)信息是存在class_data_bits_t bits這個結(jié)構(gòu)體成員變量里面的
下面探索 bits里面怎么存儲了方法、協(xié)議、屬性、成員變量等信息的,各個需要用到的結(jié)構(gòu)體的部分代碼如下,精簡便于理解,不同版本略有差異,但是流程是一樣的

1.objc_class 類結(jié)構(gòu)體部分代碼

struct objc_class : objc_object {
    // Class ISA;    // --> 繼承自objc_object --> 8字節(jié)
    Class superclass;  // --> superclass 父類指針--> 8字節(jié)
    cache_t cache;             // 方法緩存 formerly cache pointer and vtable  --> 16字節(jié)
    class_data_bits_t bits;    // 類信息存儲 class_rw_t * plus custom rr/alloc flags
    class_rw_t *data() const {
        return bits.data();
    }
}
  1. class_data_bits_t 結(jié)構(gòu)體部分代碼
struct class_data_bits_t {
    // Values are the FAST_ flags above.
    uintptr_t bits;
public:
    class_rw_t* data() const {
        return (class_rw_t *)(bits & FAST_DATA_MASK);
    }
};

  1. class_rw_t 結(jié)構(gòu)體部分代碼
struct class_rw_t {
public:
    const class_ro_t *ro() const {   //ro()  ---> 獲取存儲成員變量的相關(guān)結(jié)構(gòu)體 ---> class_ro_t
        auto v = get_ro_or_rwe();     // class_ro_t ---> 獲取成員變量方法列表
        if (slowpath(v.is<class_rw_ext_t *>())) {
            return v.get<class_rw_ext_t *>(&ro_or_rw_ext)->ro;
        }
        return v.get<const class_ro_t *>(&ro_or_rw_ext);
    }

    const method_array_t methods() const {    //methods()函數(shù) ---> 獲取方法列表
        auto v = get_ro_or_rwe();
        if (v.is<class_rw_ext_t *>()) {
            return v.get<class_rw_ext_t *>(&ro_or_rw_ext)->methods;
        } else {
            return method_array_t{v.get<const class_ro_t *>(&ro_or_rw_ext)->baseMethods()};
        }
    }

    const property_array_t properties() const {   //properties()  ---> 獲取屬性列表
        auto v = get_ro_or_rwe();
        if (v.is<class_rw_ext_t *>()) {
            return v.get<class_rw_ext_t *>(&ro_or_rw_ext)->properties;
        } else {
            return property_array_t{v.get<const class_ro_t *>(&ro_or_rw_ext)->baseProperties};
        }
    }

    const protocol_array_t protocols() const {    //獲取協(xié)議列表 
        auto v = get_ro_or_rwe();
        if (v.is<class_rw_ext_t *>()) {
            return v.get<class_rw_ext_t *>(&ro_or_rw_ext)->protocols;
        } else {
            return protocol_array_t{v.get<const class_ro_t *>(&ro_or_rw_ext)->baseProtocols};
        }
    }
};
  1. method_t 結(jié)構(gòu)體部分代碼
struct method_t {

    struct big {  // method_t 內(nèi)部又重新定義了一個 struct big 三個成員變量 SEL---types---IMP  //同 method_t
        SEL name;
        const char *types;
        MethodListIMP imp; // using MethodListIMP = IMP;
    };

public:

   big &big() const { 
        ASSERT(!isSmall());
        return *(struct big *)this;
    }

    SEL name() const { // name() ---> 獲取SEL
        if (isSmall()) {
            return (small().inSharedCache()
                    ? (SEL)small().name.get()
                    : *(SEL *)small().name.get());
        } else {
            return big().name;
        }
    }
    const char *types() const { // types() ---> 獲取方法簽名
        return isSmall() ? small().types.get() : big().types;
    }
    IMP imp(bool needsLock) const {  // imp(1) ---> 獲取IMP
        if (isSmall()) {
            IMP imp = remappedImp(needsLock);
            if (!imp)
                imp = ptrauth_sign_unauthenticated(small().imp.get(),
                                                   ptrauth_key_function_pointer, 0);
            return imp;
        }
        return big().imp;
    }
};
  1. property_t結(jié)構(gòu)體全部
struct property_t {
    const char *name; // 名稱
    const char *attributes; // 特性
};
  1. class_ro_t結(jié)構(gòu)體部分代碼
struct class_ro_t {
    const ivar_list_t * ivars; // 成員變量列表
}
  1. ivar_t結(jié)構(gòu)體部分代碼
struct ivar_t {
    const char *name; //成員變量名稱
    const char *type; // 成員變量類型
};

8.protocol_t : objc_object結(jié)構(gòu)體部分代碼,繼承自objc_object

struct protocol_t : objc_object {
    const char *mangledName; // 協(xié)議名稱
    struct protocol_list_t *protocols;
    method_list_t *instanceMethods; // 實例方法列表
    method_list_t *classMethods; // 類方法列表
    method_list_t *optionalInstanceMethods;// 可選實例方法列表
    method_list_t *optionalClassMethods;// 可選類方法列表
    property_list_t *instanceProperties;
    uint32_t size;   // sizeof(protocol_t)
    uint32_t flags;
};

核心思路 :

對應(yīng)的結(jié)構(gòu)體---> 函數(shù)(解析內(nèi)存結(jié)構(gòu)) ---> 獲取到其他結(jié)構(gòu)體 ---> 函數(shù)(解析內(nèi)存結(jié)構(gòu)) ---> 需要的結(jié)構(gòu)體 ---> 函數(shù)

10.實例方法method獲取流程

結(jié)構(gòu)體流程
objc_class(類) ---> class_data_bits_t ---> class_rw_t --->method_t

函數(shù)流程
data() ---> methods()---> name() ---> 獲取SEL
data() ---> methods()---> types() ---> 獲取types 函數(shù)簽名
data() ---> methods()---> imp(1) ---> 獲取IMP

整體流程
objc_class(類) ---> class_data_bits_t ---> data() ---> class_rw_t ---> methods() ---> method_t

method_t ---> name() --->SEL
method_t ---> types() ---> 方法簽名
method_t --- >imp(1) ---> IMP

11.類方法method獲取流程

結(jié)構(gòu)體流程
objc_class(元類) ---> class_data_bits_t ---> class_rw_t --->method_t

函數(shù)流程
data() ---> methods()---> name() ---> 獲取SEL
data() ---> methods()---> types() ---> 獲取types 函數(shù)簽名
data() ---> methods()---> imp(1) ---> 獲取IMP

整體流程
objc_class(元類) ---> class_data_bits_t ---> data() ---> class_rw_t ---> methods() ---> method_t

method_t ---> name() --->SEL
method_t ---> types() ---> 方法簽名
method_t --- >imp(1) ---> IMP

12.屬性property獲取流程

結(jié)構(gòu)體流程
objc_class(類) ---> class_data_bits_t ---> class_rw_t --->property_t

函數(shù)流程
data() ---> properties()---> name ---> 獲取屬性名稱
data() ---> properties()---> attributes ---> 獲取屬性特性attributes

整體流程
objc_class(類) ---> class_data_bits_t ---> data() ---> class_rw_t ---> properties() ---> property_t

property_t ---> name ---> 獲取屬性名稱
property_t ---> attributes ---> 獲取屬性特性attributes

13.成員變量ivar獲取流程

結(jié)構(gòu)體流程
objc_class(類) ---> class_data_bits_t ---> class_rw_t --->class_ro_t ---> ivar_t

函數(shù)流程
data() ---> ro()---> ivars ---> name 獲取屬性名稱
data() ---> ro()---> ivars ---> type 獲取屬性類型

整體流程
objc_class(類) ---> class_data_bits_t ---> data() ---> class_rw_t --->ro() ---> class_ro_t ---> ivar_t

ivars ---> name 獲取屬性名稱
ivar_t ---> type ---> 獲取屬性類型

14.協(xié)議protocol獲取流程

結(jié)構(gòu)體流程
objc_class(類) ---> class_data_bits_t ---> class_rw_t --->protocol_t(對象) ---> method_t

函數(shù)流程
data() ---> protocols()---> instanceMethods ---> 獲取實例方法列表
instanceMethods ---> name() --->SEL
instanceMethods ---> types() --->方法簽名
instanceMethods ---> imp(1) --->IMP

同上
data() ---> protocols()---> classMethods ---> 獲取類方法列表
data() ---> protocols()---> optionalInstanceMethods ---> 獲取獲取可選實例方法
data() ---> protocols()---> optionalClassMethods ---> 獲取獲取可選類方法

整體流程
objc_class(類) ---> class_data_bits_t ---> data() ---> class_rw_t ---> protocols() ---> protocol_t(對象) ---> instanceMethods

instanceMethods ---> name() --->SEL
instanceMethods ---> types() --->方法簽名
instanceMethods ---> imp(1) --->IMP

15.LLDB調(diào)試實例方法method解析


LLDB調(diào)試-bits實例方法method解析.jpeg

16.LLDB調(diào)試類方法method解析


LLDB調(diào)試-bits類方法method解析.jpeg

17.LLDB調(diào)試屬性property解析


LLDB調(diào)試-bits屬性property解析.jpeg

18.LLDB調(diào)試成員變量ivar解析


LLDB調(diào)試-bits-成員變量ivar解析.jpeg

19.LLDB調(diào)試協(xié)議protocol解析


LLDB調(diào)試-bits-協(xié)議protocol解析1.jpeg
LLDB調(diào)試-bits-協(xié)議protocol解析2.jpeg

20.LLDB調(diào)試-實例方法instanceMethod調(diào)試過程

KCObjcBuild was compiled with optimization - stepping may behave oddly; variables may not be available.
(lldb) p/x CJLPerson.class
(Class) $0 = 0x00000001000081c0 CJLPerson
(lldb) p (class_data_bits_t *) 0x00000001000081e0
(class_data_bits_t *) $1 = 0x00000001000081e0
(lldb) p $1->data()
(class_rw_t *) $2 = 0x0000000101119d10
(lldb) p $2->methods()
(const method_array_t) $3 = {
  list_array_tt<method_t, method_list_t, method_list_t_authed_ptr> = {
     = {
      list = {
        ptr = 0x0000000100003ea0
      }
      arrayAndFlag = 4294983328
    }
  }
}
(lldb) p $3.list
(const method_list_t_authed_ptr<method_list_t>) $4 = {
  ptr = 0x0000000100003ea0
}
(lldb) p $4.ptr
(method_list_t *const) $5 = 0x0000000100003ea0
(lldb) p *$5
(method_list_t) $6 = {
  entsize_list_tt<method_t, method_list_t, 4294901763, method_t::pointer_modifier> = (entsizeAndFlags = 2147483660, count = 5)
}
(lldb) p $6.get(0).name()
(SEL) $7 = "sayHello:"
(lldb) p $6.get(0).types()
(const char *) $8 = 0x0000000100003f7a "v24@0:8@16"
(lldb) p $6.get(0).imp(1)
(IMP) $9 = 0x0000000100003be0 (KCObjcBuild`-[CJLPerson sayHello:] at main.m:34)
(lldb) p $6.get(1).name()
(SEL) $10 = "sayWorld"
(lldb) p $6.get(1).types()
(const char *) $11 = 0x0000000100003f66 "v16@0:8"
(lldb) p $6.get(1).imp(1)
(IMP) $12 = 0x0000000100003c1c (KCObjcBuild`-[CJLPerson sayWorld] at main.m:38)
(lldb) p $6.get(2).name()
(SEL) $13 = "cjl_name"
(lldb) p $6.get(2).types()
(const char *) $14 = 0x0000000100003f85 "@16@0:8"
(lldb) p $6.get(2).imp(1)
(IMP) $15 = 0x0000000100003c30 (KCObjcBuild`-[CJLPerson cjl_name] at main.m:24)
(lldb) p $6.get(3).name()
(SEL) $16 = "setCjl_name:"
(lldb) p $6.get(3).types()
(const char *) $17 = 0x0000000100003f7a "v24@0:8@16"
(lldb) p $6.get(3).imp(1)
(IMP) $18 = 0x0000000100003c54 (KCObjcBuild`-[CJLPerson setCjl_name:] at main.m:24)
(lldb) p $6.get(4).name()
(SEL) $19 = ".cxx_destruct"
(lldb) p $6.get(5).name()
Assertion failed: (i < count), function get, file /Users/objc M1和因特爾芯片源碼/objc源碼/M1芯片mac/objc4-818.2/runtime/objc-runtime-new.h, line 624.
error: Execution was interrupted, reason: signal SIGABRT.
The process has been returned to the state before expression evaluation.
(lldb)

LLDB調(diào)試-類方法classMethod調(diào)試過程

KCObjcBuild was compiled with optimization - stepping may behave oddly; variables may not be available.
(lldb) p/x CJLPerson.class
(Class) $0 = 0x0000000100008200 CJLPerson
(lldb) x/2gx 0x0000000100008200
0x100008200: 0x00000001000081d8 0x0000000100379140
(lldb) po 0x00000001000081d8
CJLPerson

(lldb) p (class_data_bits_t *) 0x00000001000081f8
(class_data_bits_t *) $2 = 0x00000001000081f8
(lldb) p $2->data()
(class_rw_t *) $3 = 0x0000000101029b40
(lldb) p $3->methods()
(const method_array_t) $4 = {
  list_array_tt<method_t, method_list_t, method_list_t_authed_ptr> = {
     = {
      list = {
        ptr = 0x0000000100003e28
      }
      arrayAndFlag = 4294983208
    }
  }
}
(lldb) p $4.list
(const method_list_t_authed_ptr<method_list_t>) $5 = {
  ptr = 0x0000000100003e28
}
(lldb) p $5.ptr
(method_list_t *const) $6 = 0x0000000100003e28
(lldb) p *$6
(method_list_t) $7 = {
  entsize_list_tt<method_t, method_list_t, 4294901763, method_t::pointer_modifier> = (entsizeAndFlags = 2147483660, count = 2)
}
(lldb) p $7.get(0).name()
(SEL) $8 = "sayBye"
(lldb) p $7.get(0).types()
(const char *) $9 = 0x0000000100003f5a "v16@0:8"
(lldb) p $7.get(0).imp(1)
(IMP) $10 = 0x0000000100003adc (KCObjcBuild`+[CJLPerson sayBye] at main.m:42)
(lldb) p $7.get(1).name()
(SEL) $11 = "sayLove"
(lldb) p $7.get(1).types()
(const char *) $12 = 0x0000000100003f5a "v16@0:8"
(lldb) p $7.get(1).imp(1)
(IMP) $13 = 0x0000000100003af0 (KCObjcBuild`+[CJLPerson sayLove] at main.m:46)
(lldb) p $7.get(2).name()
Assertion failed: (i < count), function get, file /Users/objc M1和因特爾芯片源碼/objc源碼/M1芯片mac/objc4-818.2/runtime/objc-runtime-new.h, line 624.
error: Execution was interrupted, reason: signal SIGABRT.
The process has been returned to the state before expression evaluation.
(lldb)

LLDB調(diào)試-屬性property調(diào)試過程

KCObjcBuild was compiled with optimization - stepping may behave oddly; variables may not be available.
(lldb) p/x CJLPerson.class
(Class) $0 = 0x0000000100008208 CJLPerson
(lldb) p (class_data_bits_t *) 0x0000000100008228
(class_data_bits_t *) $1 = 0x0000000100008228
(lldb) p $1->data()
(class_rw_t *) $2 = 0x00000001008570f0
(lldb) p $2->properties()
(const property_array_t) $3 = {
  list_array_tt<property_t, property_list_t, RawPtr> = {
     = {
      list = {
        ptr = 0x0000000100008158
      }
      arrayAndFlag = 4295000408
    }
  }
}
(lldb) p $3.list
(const RawPtr<property_list_t>) $4 = {
  ptr = 0x0000000100008158
}
(lldb) p $4.ptr
(property_list_t *const) $5 = 0x0000000100008158
(lldb) p *$5
(property_list_t) $6 = {
  entsize_list_tt<property_t, property_list_t, 0, PointerModifierNop> = (entsizeAndFlags = 16, count = 2)
}
(lldb) p $6.get(0).name
(const char *) $7 = 0x0000000100003f1c "cjl_name"
(lldb) p $6.get(0).attributes
(const char *) $8 = 0x0000000100003eb7 "T@\"NSString\",C,N,V_cjl_name"
(lldb) p $6.get(1).name
(const char *) $9 = 0x0000000100003f32 "number"
(lldb) p $6.get(1).attributes
(const char *) $10 = 0x0000000100003ed3 "T@\"NSString\",C,N,V_number"
(lldb) p $6.get(2).name
Assertion failed: (i < count), function get, file /Users/objc M1和因特爾芯片源碼/objc源碼/M1芯片mac/objc4-818.2/runtime/objc-runtime-new.h, line 624.
error: Execution was interrupted, reason: signal SIGABRT.
The process has been returned to the state before expression evaluation.
(lldb)

LLDB調(diào)試-成員變量ivar調(diào)試過程

KCObjcBuild was compiled with optimization - stepping may behave oddly; variables may not be available.
(lldb) p/x CJLPerson.class
(Class) $0 = 0x0000000100008200 CJLPerson
(lldb) p (class_data_bits_t *) 0x0000000100008220
(class_data_bits_t *) $1 = 0x0000000100008220
(lldb) p $1->data()
(class_rw_t *) $2 = 0x0000000105137810
(lldb) p $2->ro()
(const class_ro_t *) $3 = 0x0000000100008080
(lldb) p $3->ivars
(const ivar_list_t *const) $4 = 0x00000001000080c8
(lldb) p *$4
(const ivar_list_t) $5 = {
  entsize_list_tt<ivar_t, ivar_list_t, 0, PointerModifierNop> = (entsizeAndFlags = 32, count = 4)
}
(lldb) p $5.get(0).name
(const char *) $6 = 0x0000000100003ef5 "hobby"
(lldb) p $5.get(0).type
(const char *) $7 = 0x0000000100003f62 "@\"NSString\""
(lldb) p $5.get(1).name
(const char *) $8 = 0x0000000100003efb "sex"
(lldb) p $5.get(1).type
(const char *) $9 = 0x0000000100003f62 "@\"NSString\""
(lldb) p $5.get(2).name
(const char *) $10 = 0x0000000100003eff "_cjl_name"
(lldb) p $5.get(2).type
(const char *) $11 = 0x0000000100003f62 "@\"NSString\""
(lldb) p $5.get(3).name
(const char *) $12 = 0x0000000100003f09 "_number"
(lldb) p $5.get(3).type
(const char *) $13 = 0x0000000100003f62 "@\"NSString\""
(lldb) p $5.get(4).name
Assertion failed: (i < count), function get, file /Users/objc M1和因特爾芯片源碼/objc源碼/M1芯片mac/objc4-818.2/runtime/objc-runtime-new.h, line 624.
error: Execution was interrupted, reason: signal SIGABRT.
The process has been returned to the state before expression evaluation.
(lldb)

LLDB調(diào)試-協(xié)議protocol調(diào)試過程

KCObjcBuild was compiled with optimization - stepping may behave oddly; variables may not be available.
(lldb) p/x CJLPerson.class
(Class) $0 = 0x0000000100008600 CJLPerson
(lldb) p (class_data_bits_t *) 0x0000000100008620
(class_data_bits_t *) $1 = 0x0000000100008620
(lldb) p $1->data()
(class_rw_t *) $2 = 0x0000000100957f50
(lldb) p $2->protocols()
(const protocol_array_t) $3 = {
  list_array_tt<unsigned long, protocol_list_t, RawPtr> = {
     = {
      list = {
        ptr = 0x0000000100008410
      }
      arrayAndFlag = 4295001104
    }
  }
}
(lldb) p $3.list
(const RawPtr<protocol_list_t>) $4 = {
  ptr = 0x0000000100008410
}
(lldb) p $4.ptr
(protocol_list_t *const) $5 = 0x0000000100008410
(lldb) p *$5
(protocol_list_t) $6 = (count = 1, list = protocol_ref_t [] @ 0x000000012921a7c8)
(lldb) p $6.list[0]
(protocol_ref_t) $7 = 4295001744
(lldb) p (protocol_t *)$7
(protocol_t *) $8 = 0x0000000100008690
(lldb) p *$8
(protocol_t) $9 = {
  objc_object = {
    isa = {
      bits = 4298608840
      cls = Protocol
       = {
        nonpointer = 0
        has_assoc = 0
        has_cxx_dtor = 0
        shiftcls = 537326105
        magic = 0
        weakly_referenced = 0
        unused = 0
        has_sidetable_rc = 0
        extra_rc = 0
      }
    }
  }
  mangledName = 0x0000000100003c4d "CJLPersonDelegate"
  protocols = 0x0000000100008310
  instanceMethods = 0x0000000100008328
  classMethods = 0x0000000100008348
  optionalInstanceMethods = 0x0000000100008368
  optionalClassMethods = 0x0000000100008388
  instanceProperties = nil
  size = 96
  flags = 0
  _extendedMethodTypes = 0x00000001000083a8
  _demangledName = 0x0000000000000000
  _classProperties = nil
}
(lldb) p/x 4298608840
(long) $10 = 0x00000001003790c8
(lldb) p/x 0x00000001003790c8 & 0x0000000ffffffff8ULL
(unsigned long long) $11 = 0x00000001003790c8
(lldb) po 0x00000001003790c8
Protocol

(lldb) p $9.instanceMethods
(method_list_t *) $13 = 0x0000000100008328
(lldb) p *$13
(method_list_t) $14 = {
  entsize_list_tt<method_t, method_list_t, 4294901763, method_t::pointer_modifier> = (entsizeAndFlags = 24, count = 1)
}
(lldb) p $14.get(0).name()
(SEL) $15 = "sendDataToController:"
(lldb) p $14.get(0).types()
(const char *) $16 = 0x0000000100003d50 "v24@0:8@16"
(lldb) p $14.get(0).imp(1)
(IMP) $17 = 0x0000000000000000
(lldb) p $9.classMethods
(method_list_t *) $18 = 0x0000000100008348
(lldb) p *$18
(method_list_t) $19 = {
  entsize_list_tt<method_t, method_list_t, 4294901763, method_t::pointer_modifier> = (entsizeAndFlags = 24, count = 1)
}
(lldb) p $19.get(0).name()
(SEL) $20 = "sendString:"
(lldb) p $19.get(0).types()
(const char *) $21 = 0x0000000100003d50 "v24@0:8@16"
(lldb) p $19.get(0).imp(1)
(IMP) $22 = 0x0000000000000000
(lldb) p $9.optionalInstanceMethods
(method_list_t *) $23 = 0x0000000100008368
(lldb) p *$23
(method_list_t) $24 = {
  entsize_list_tt<method_t, method_list_t, 4294901763, method_t::pointer_modifier> = (entsizeAndFlags = 24, count = 1)
}
(lldb) p $24.get(0).name()
(SEL) $25 = "showKeyboard"
(lldb) p $24.get(0).types()
(const char *) $26 = 0x0000000100003d48 "v16@0:8"
(lldb) p $24.get(0).imp(1)
(IMP) $27 = 0x0000000000000000
(lldb) p $9.optionalClassMethods
(method_list_t *) $28 = 0x0000000100008388
(lldb) p *$28
(method_list_t) $29 = {
  entsize_list_tt<method_t, method_list_t, 4294901763, method_t::pointer_modifier> = (entsizeAndFlags = 24, count = 1)
}
(lldb) p $29.get(0).name()
(SEL) $30 = "closeKeyboard"
(lldb) p $29.get(0).types()
(const char *) $31 = 0x0000000100003d48 "v16@0:8"
(lldb) p $29.get(0).imp(1)
(IMP) $32 = 0x0000000000000000
(lldb)

bits存儲類信息的共同點
無論方法、屬性、協(xié)議、成員變量都放在了對應(yīng)結(jié)構(gòu)體的數(shù)組里,method_list_t、property_list_t、ivar_list_t、protocol_list_t,說明這些是有序而且是值為真

LLDB調(diào)試獲取xxxx_list_t里面的元素方式是p $5.get(0) p $5.get(1) ... p $5.get(N)get(index)下標取法(圓括號+下標

那么接下來objc_class里面非常重要的cache是不是也是這樣的存儲方式呢?

(method_list_t) $6 = {
  entsize_list_tt<method_t, method_list_t, 4294901763, method_t::pointer_modifier> = (entsizeAndFlags = 2147483660, count = 5)
}

(property_list_t) $6 = {
  entsize_list_tt<property_t, property_list_t, 0, PointerModifierNop> = (entsizeAndFlags = 16, count = 2)
}

(const ivar_list_t) $5 = {
  entsize_list_tt<ivar_t, ivar_list_t, 0, PointerModifierNop> = (entsizeAndFlags = 32, count = 4)
}

(protocol_list_t) $6 = (count = 1, list = protocol_ref_t [] @ 0x000000012921a7c8)

3.isa、superclass、bits總結(jié)

** objc_class、objc_objcet、Class**
1.Classobjc_class,來自于底層objc_class結(jié)構(gòu)體
2.idobjc_object對象,即對象來自于objc_object結(jié)構(gòu)體
3.objc_class : objc_objectobjc_class繼承自objc_object結(jié)構(gòu)體,objc_classisa繼承自objc_object,所以,和對象都有Class isa指針
4.superclass有繼承關(guān)系,對象沒有superclass沒有繼承關(guān)系

元類
1.我們都知道 對象isa 是指向其實也是一個對象,可以稱為類對象,其isa指向蘋果定義的元類
2.元類系統(tǒng)給的,其定義創(chuàng)建都是由編譯器完成,在這個過程中,的歸屬來自于元類
3.元類 是類對象 的類,每個都有一個獨一無二的元類用來存儲 類方法的相關(guān)信息。
元類本身是沒有名稱的,由于與相關(guān)聯(lián),所以使用了同名一樣的名稱,但是本質(zhì)和底層編譯的名稱是不一樣的
4.CJLPerson不是CJLPerson_metaClass(CJLPerson元類),內(nèi)存地址不同,但是每一個只有一份
5.NSobject不是NSobject_metaClass(根元類),內(nèi)存地址不同,但是每一個只有一份

** isa**
對象 ---> ---> 元類 ---> NSobject(根元類) ---> NSobject(根元類)

superclass
1.只有類有繼承關(guān)系,對象沒有繼承關(guān)系
子類 ---> 父類 ---> ...---> NSObject ---> nil
子元類 --->父元類 ---> ...---> NSObject(根元類) ---> NSObject ---> nil

bits
1.實例方法里面,類方法元類里面,類方法相當(dāng)于元類實例方法,由于只有實例方法類方法,所以,根元類里面是沒有方法列表
2.成員變量列表里面有屬性屬性列表里面沒有成員變量
3.實例方法列表包含屬性settergetter方法
4.實例對象來自于objc_object 結(jié)構(gòu)體
5.實例方法、類方法協(xié)議、屬性成員變量都存在對應(yīng)或者元類objc_class --->class_data_bits_t bits成員變量里面
6.協(xié)議是一個對象繼承自objc_object 結(jié)構(gòu)體,協(xié)議里面通過各種方法列表存儲協(xié)議方法

問題
1.Class到底是什么?Class之間的關(guān)系?
Class就是,也是類的首地址-指針,存儲內(nèi)容是類名,類似于數(shù)組名和數(shù)組首地址

2.對象isaisa之間的區(qū)別?分別存儲的是什么值?
類的isa 64位中只在對應(yīng)的33位存儲類的首地址,對象的isa 64位中除了類信息還有其他信息,nonpointer = 0的話,對象isa只存儲類信息,但是多數(shù)對象是nonpointer = 1的

3.對象方法存儲在中,那類方法存儲在哪里呢?如果一直有相應(yīng)的來存儲那么盡頭在哪里呢?
對象方法存儲在中,類方法存儲在元類中,由于只有類方法類方法,所以,根元類里面是沒有方法列表的

4.isasuperclass是什么關(guān)系? isa到底有哪些作用?
isasuperclass都是Class類型的64位指針,都是存儲類的指針,沒有直接的關(guān)系,isa偏向于數(shù)據(jù)的操作邏輯屬于誰,即方法存儲在哪里,找到后去操作數(shù)據(jù),superclass偏向于父類在哪里,即更高級的操作邏輯屬于誰,superclass是幫助isa在不同層級的父類中查找操作邏輯

5.在內(nèi)存中到底存在幾份
在內(nèi)存中只有一份,元類雖然看著名字一樣,但是本質(zhì)是兩個獨立的類,內(nèi)存空間底層編譯都是不同的

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

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