runtime

內(nèi)存對(duì)齊

1、數(shù)據(jù)成員對(duì)?規(guī)則:結(jié)構(gòu)(struct)(或聯(lián)合(union))的數(shù)據(jù)成員,第一個(gè)數(shù)據(jù)成員放在offset為0的地方,以后每個(gè)數(shù)據(jù)成員存儲(chǔ)的起始位置要從該成員大小或者成員的子成員大小(只要該成員有子成員,比如說是數(shù)組,結(jié)構(gòu)體等)的整數(shù)倍開始(比如int為4字節(jié),則要從4的整數(shù)倍地址開始存儲(chǔ))。
2、結(jié)構(gòu)體作為成員:如果一個(gè)結(jié)構(gòu)里有某些結(jié)構(gòu)體成員,則結(jié)構(gòu)體成員要從其內(nèi)部最大元素大小的整數(shù)倍地址開始存儲(chǔ).(struct a里存有struct b,b里有char,int ,double等元素,那b應(yīng)該從8的整數(shù)倍開始存儲(chǔ).)
3、收尾工作:結(jié)構(gòu)體的總大小,也就是sizeof的結(jié)果,.必須是其內(nèi)部最大成員(基本數(shù)據(jù)類型)的整數(shù)倍.不足的要補(bǔ)?。
class_getInstanceSize(Class cls)方法, 查看一個(gè)對(duì)象中, 所有成員變量占用的內(nèi)存大小, (內(nèi)存對(duì)齊后的)
malloc_size(const void *ptr)方法, 方法查看操作系統(tǒng)實(shí)際分配的內(nèi)存空間. 且一定是16的整數(shù)倍.

Type Encodings

v24@0:8@16的含義也就是:

  • v:返回值void
  • 24:參數(shù)一共占用24字節(jié).
  • @: 對(duì)象類型參數(shù)self
  • 0:上面參數(shù)從0位置開始
  • :: SEL
  • 8:SEL從8位置開始
  • @:對(duì)象類型,實(shí)際傳入的第一個(gè)參數(shù)
  • 16:從16位置開始

superClass 和 isa

20210316124236753.png
  1. instance調(diào)用對(duì)象方法的軌跡
    1. isa找到class, 方法不存在, 就通過superclass找父類
  2. class調(diào)用類方法的軌跡
    1. isa找meta-class, 方法不存在, 就通過superclass找父類
@interface NSObject (Test)
+ (void)test;
@end
@implementation NSObject (Test)
- (void)test {
    NSLog(@"- [NSObject test] - %p", self);
}
@end
[Person test];

源碼

typedef struct objc_class *Class;
typedef struct objc_object *id;
typedef struct objc_selector *SEL;
typedef struct objc_method *Method;
typedef struct objc_ivar *Ivar;
typedef id (*IMP)(id, SEL, ...);

struct objc_object {
private:
    isa_t isa;
}
union isa_t {
    Class cls;
    uintptr_t bits;
    struct {
        ISA_BITFIELD;
    };
};
# define ISA_BITFIELD                                                          \
        uintptr_t nonpointer        : 1; 表示是否對(duì) isa 指針開啟指針優(yōu)化 0:純isa指針,1:不止是類對(duì)象地址**,isa** 中包含了類信息、對(duì)象的引用計(jì)數(shù)等
        uintptr_t has_assoc         : 1; 關(guān)聯(lián)對(duì)象標(biāo)志位,0沒有,1存在
        uintptr_t has_cxx_dtor      : 1;該對(duì)象是否有 C++ 或者 Objc 的析構(gòu)器**,如果有析構(gòu)函數(shù),則需要做析構(gòu)邏輯,** 如果沒有**,**則可以更快的釋放對(duì)象
        uintptr_t shiftcls          : 33;存儲(chǔ)類指針的值。開啟指針優(yōu)化的情況下,在 arm64 架構(gòu)中有 33 位用來存儲(chǔ)類指針。
        uintptr_t magic             : 6; 用于調(diào)試器判斷當(dāng)前對(duì)象是真的對(duì)象還是沒有初始化的空間
        uintptr_t weakly_referenced : 1;對(duì)象是否被指向或者曾經(jīng)指向一個(gè) ARC 的弱變量,沒有弱引用的對(duì)象可以更快釋放
        uintptr_t deallocating            : 1;  標(biāo)志對(duì)象是否正在釋放內(nèi)存
        uintptr_t has_sidetable_rc  : 1; :當(dāng)對(duì)象引用技術(shù)大于 10 時(shí),則需要借用該變量存儲(chǔ)進(jìn)位
        uintptr_t extra_rc          : 19 當(dāng)表示該對(duì)象的引用計(jì)數(shù)值,實(shí)際上是引用計(jì)數(shù)值減 1, 例如,如果對(duì)象的引用計(jì)數(shù)為 10,那么 extra_rc 為 9。如果引用計(jì)數(shù)大于 10, 則需要使用到下面的 has_sidetable_rc。

struct objc_class : objc_object {
    Class superclass;
    cache_t cache;
    class_data_bits_t bits;
    class_rw_t *data() { 
        return bits.data();
    }
}

struct class_data_bits_t {
    class_rw_t* data() const {
        return (class_rw_t *)(bits & FAST_DATA_MASK);
    }
    const class_ro_t *safe_ro() const {
        class_rw_t *maybe_rw = data();
        if (maybe_rw->flags & RW_REALIZED) {
            // maybe_rw is rw
            return maybe_rw->ro();
        } else {
            // maybe_rw is actually ro
            return (class_ro_t *)maybe_rw;
        }
    }
}

struct class_rw_t {
    const method_array_t methods() const {}
    const property_array_t properties() const {}
    const protocol_array_t protocols() const {}
}

struct class_ro_t {
    void *baseMethodList;
    protocol_list_t * baseProtocols;
    const ivar_list_t * ivars;

    const uint8_t * weakIvarLayout;
    property_list_t *baseProperties
}


template <typename Element, typename List, template<typename> class Ptr>
class list_array_tt {
    union {
        Ptr<List> list; // - list.ptr 中存儲(chǔ)著 Element類型的數(shù)據(jù)
        uintptr_t arrayAndFlag;
    };
};

template <typename Element, typename List, uint32_t FlagMask, typename PointerModifier = PointerModifierNop>
struct entsize_list_tt {
  Element& get(uint32_t i) const { }
}


class method_array_t :  public list_array_tt <method_t, method_list_t, method_list_t_authed_ptr>
class property_array_t :  public list_array_tt<property_t, property_list_t, RawPtr>
class protocol_array_t :  public list_array_tt<protocol_ref_t, protocol_list_t, RawPtr>


struct method_list_t : entsize_list_tt<method_t, method_list_t, 0xffff0003, method_t::pointer_modifier> {}
struct property_list_t : entsize_list_tt<property_t, property_list_t, 0> {};
struct ivar_list_t : entsize_list_tt<ivar_t, ivar_list_t, 0> {}

struct property_t {
    const char *name;
    const char *attributes;
};

struct ivar_t {
    int32_t *offset;
    const char *name;
    const char *type;
    uint32_t alignment_raw;
    uint32_t size;
}

struct method_t {
    struct big {
        SEL name;
        const char *types;
        MethodListIMP imp;
    }
}

假設(shè)類對(duì)象的地址是 p
1. 那么 p + 0x20(isa 8 字節(jié), superclass 8 字節(jié), cache : 16字節(jié), 共 32 = 0x20 字節(jié)) == bits地址;
2. bits->data () 拿到 class_rw_t* rw .
  1. 屬性
    1. (*rw).properties()  返回 protocol_array_t protoclsArray;
    2. protoclsArray.list 返回 RawPtr<property_list_t> rowPtr;
    3. rowPtr.ptr 返回 property_list_t* list;
    4. (*list).get(0) 返回property_t類型
    property_t : (name = "name", attributes = "T@\"NSString\",C,N,V_name")

  2. 實(shí)例方法
    1. (*rw).methods()  返回 method_array_t methodsArray;
    2. methodsArray.list 返回 method_list_t_authed_ptr<method_list_t>) authedPtr;
    3. authedPtr.ptr 返回 method_list_t* list;
    4. list.get(0) 返回method_t類型
    5. method_t.big() 查看結(jié)構(gòu)體
    method_t::big:  { name = "sayNB", types = 0x0000000100003f71 "v16@0:8", imp = 0x0000000100003b50 (KCObjcBuild`-[JSPerson sayNB])}
 
3. bits-> safe_ro () 拿到 class_ro_t* ro .
  1. 成員變量
    1. (*ro).ivars 返回 ivar_list_t *ivarList;
    2. (*ivarList).get(0) 返回 ivar_t
    ivar_t :  {offset = 0x00000001000084d8, name = 0x0000000100003f18 "nickName", type = 0x0000000100003f79 "@\"NSString\"", alignment_raw = 3, size = 8}

4. 元類對(duì)象地址 *p;那么元類對(duì)象的地址就是 *p + 0x20 = bits.
1. bits->data () 拿到 class_rw_t* rw .
  1. 類方法
      1. (*rw).methods()  返回 method_array_t methodsArray;
      2. methodsArray.list 返回 method_list_t_authed_ptr<method_list_t>) authedPtr;
      3. authedPtr.ptr 返回 method_list_t* list;
      4. list.get(0) 返回method_t類型
      5. method_t.big() 查看結(jié)構(gòu)體
      method_t::big:  { name = "sayNB", types = 0x0000000100003f71 "v16@0:8", imp = 0x0000000100003b50 (KCObjcBuild`-[JSPerson sayNB])}

class_rw_t

  1. 在運(yùn)行時(shí)生成, 它會(huì)先將class_ro_t的內(nèi)容拷貝過去,然后再將當(dāng)前類的分類的這些屬性、方法等拷貝到其中。所以可以說class_rw_t是class_ro_t的超集,當(dāng)然實(shí)際訪問類的方法、屬性等也都是訪問的class_rw_t中的內(nèi)容。
  2. 存儲(chǔ)屬性、方法、協(xié)議等信息
  3. 可讀可寫

class_ro_t

  1. 存儲(chǔ)了當(dāng)前類在編譯期就已經(jīng)確定的屬性、方法和遵循的協(xié)議,里面沒有category的方法
  2. 存儲(chǔ)了成員變量、baseMethodList、baseProtocols、baseProperties等信息
  3. 只讀

總結(jié)

  1. 實(shí)例方法, 成員變量存儲(chǔ)在類中.
  2. 類方法, 存儲(chǔ)在元類中.

category

struct category_t {
    const char *name;
    classref_t cls;
    WrappedPtr<method_list_t, PtrauthStrip> instanceMethods;
    WrappedPtr<method_list_t, PtrauthStrip> classMethods;
    struct protocol_list_t *protocols;
    struct property_list_t *instanceProperties;
    struct property_list_t *_classProperties;
};
  • 可以添加實(shí)例方法,類方法,甚至可以實(shí)現(xiàn)協(xié)議,添加屬性
  • 無法添加實(shí)例變量

將分類中的方法/屬性/合并到類對(duì)象中

  1. 調(diào)用 attachCategories
    1.獲取所有的分類列表的count
    1. 創(chuàng)建三個(gè)數(shù)組, method_list_t **mlists; property_list_t **proplists; protocol_list_t **protolists;
    2. 使用count, 遍歷到每個(gè)分類, 將分類中的方法, 屬性, 協(xié)議添加到三個(gè)數(shù)組中.
  2. 調(diào)用 attachLists
    1. 將原本 rw的 rw->methods, rw->properties, rw->protocols 后移 count 個(gè)字節(jié).
    2. 將mlists, proplists, protolists 中的方法, 屬性, 協(xié)議添加到rw->methods, rw->properties, ->protocols 前.

結(jié)論

所以, 如果分類實(shí)現(xiàn)了主類的方法, 會(huì)調(diào)用分類的方法, 而不調(diào)用主類的方法, 因?yàn)榉诸惖姆椒ㄔ谇斑? 方法查找的時(shí)候, 先找到了分類的方法就直接執(zhí)行了, 不會(huì)再繼續(xù)查找了. 而如果多個(gè)分類同時(shí)實(shí)現(xiàn)了同名方法, 就會(huì)查找到后編譯的分類的方法.

將 父類, 父類分類, 子類, 子類分類 同時(shí)實(shí)現(xiàn)了+load方法

  1. 先調(diào)用類的+load方法,然后調(diào)用子類的+load方法, 然后調(diào)用父類分類和子類分類的+load方法;(分類的+load方法, 按照先后順序, 先編譯的先調(diào)用, 后編譯的后調(diào)用);
  2. 如果子類沒有實(shí)現(xiàn)load, 但是子類的分類實(shí)現(xiàn)了load方法, 那么就會(huì)先調(diào)用父類的load方法, 然后按照子類的分類和父類的分類的編譯順序, 調(diào)用load方法.
  3. 子類和子類的分類不實(shí)現(xiàn)load方法, 然后用子類主動(dòng)調(diào)用load方法 [Son load], 首先會(huì)走流程2, 即 父類,的load方法, 父類的分類(先編譯)的load方法, 父類的分類(后編譯)的load方法, 然后走msg_send()流程調(diào)用 父類的分類(后編譯)的load方法

將 父類, 父類分類, 子 類, 子類分類同時(shí)實(shí)現(xiàn)了+initialize方法

+initialize調(diào)用時(shí)機(jī) : 當(dāng)一個(gè)類在查找方法的時(shí)候, 會(huì)先判斷當(dāng)前類是否初始化, 如果沒有初始化就會(huì)去掉用initialize方法, 如果這個(gè)類的父類沒有初始化, 就會(huì)先調(diào)用父類的initialize方法, 再調(diào)用自己的initialize方法,類在調(diào)用initialize時(shí), 使用的是objc_msgSend消息機(jī)制調(diào)用.
alloc -> class_getInstanceMethod -> lookUpImpOrForward -> _class_initialize(不斷向父類遞歸) -> callInitialize -> objc_msgSend(cls, SEL_initialize);

  1. 如果父類調(diào)用 +initialize, 如果父類的+initialize方法同時(shí)被主類和分類實(shí)現(xiàn), 那么就只會(huì)調(diào)用父類分類的+initialize ,如果多個(gè)父類分類同時(shí)實(shí)現(xiàn), 那么調(diào)用后編譯的分類的 +initialize的實(shí)現(xiàn);
  2. 如果子類調(diào)用 +initialize
    1. 子類/子類分類實(shí)現(xiàn)了 +initialize
      1. 調(diào)用父類 +initialize, 如果父類的+initialize方法同時(shí)被主類和分類實(shí)現(xiàn), 那么就只會(huì)調(diào)用父類分類的+initialize ,如果多個(gè)父類分類同時(shí)實(shí)現(xiàn), 那么調(diào)用后編譯的分類的 +initialize的實(shí)現(xiàn);
      2. 用子類 +initialize, 如果子類的+initialize方法同時(shí)被主類和分類實(shí)現(xiàn), 那么就只會(huì)調(diào)用子類分類的+initialize ,如果多個(gè)子類分類同時(shí)實(shí)現(xiàn), 那么調(diào)用后編譯的分類的 +initialize的實(shí)現(xiàn);
    2. 類/子類分類都沒有實(shí)現(xiàn)了 +initialize
      第一次是因?yàn)楦割悰]有初始化, 所以會(huì)調(diào)用父類的+initialize , 第二次調(diào)用父類的+initialize是因?yàn)檎{(diào)用子類的+initialize方法, 但是子類沒有實(shí)現(xiàn), 所以調(diào)用了父類的 +initialize;
      1. 就會(huì)調(diào)用兩次父類的 +initialize, 如果父類的+initialize方法同時(shí)被主類和分類實(shí)現(xiàn), 那么就會(huì)調(diào)用兩次父類分類的+initialize ,如果多個(gè)父類分類同時(shí)實(shí)現(xiàn), 那么調(diào)用兩次后編譯的分類的 +initialize的實(shí)現(xiàn);

extension

  1. 聲明私有屬性
  2. 聲明私有方法
  3. 聲明私有成員變量
  4. 編譯時(shí)決議(在編譯的時(shí)候就將擴(kuò)展的所有數(shù)據(jù)都合并到類中去了)
  5. 只能聲明, 不能實(shí)現(xiàn)
  6. 不能為系統(tǒng)類添加擴(kuò)展.

關(guān)聯(lián)屬性

iShot2022-02-15 23.56.09.png
  1. _object_set_associative_reference 用來添加關(guān)聯(lián)對(duì)象
  2. objc_destructInstance 中調(diào)用 _object_remove_assocations 移除關(guān)聯(lián)對(duì)象.
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容