對象的本質(zhì) Class結(jié)構(gòu) + load + initialize 對象動態(tài)綁定 __weak底層實現(xiàn)

對象的本質(zhì)

NSObject * obj = [[NSObject alloc] init];
轉(zhuǎn)化成c++可以到本質(zhì)是一個結(jié)構(gòu)體

struct NSObject_IMPL{
  Class isa.
};
typedef struct objc_class * Class; 

可以看出isa 是指向objc_class結(jié)構(gòu)的指針
obj對象的地址,就是結(jié)構(gòu)體的地址,結(jié)構(gòu)體里面第一個元素就是isa,結(jié)構(gòu)體的地址也就是isa指針的地址.
總結(jié)對象的本質(zhì)就是一個指向類結(jié)構(gòu)體的指針 我們NSLog(@"%@", obj)其實是打印了一個 指向NSObject這個類的指針的地址.
下面是一個Student轉(zhuǎn)成的c++

struct Student_IMPL{
struct NSObject_IMPL NSObject_IVARS;
int _num
int _age
};
image.png
struct lc_struct {
    Class isa;
};
/*
     if __arm64__
     #   define ISA_MASK        0x0000000ffffffff8ULL
     # elif __x86_64__
     #   define ISA_MASK        0x00007ffffffffff8ULL
     */
 LCKVCtest *kvc = [[LCKVCtest alloc]init];
    Class class = kvc.class;
    Class mClass = object_getClass(class);
    Class mObjectClass = object_getClass([NSObject class]);
    Class mObjectClass2 = object_getClass(mObjectClass);
    
    NSLog(@"kvc = %p",kvc);    //對象指向的地址
    NSLog(@"class = %p",class);  //類對象指向的地址
    NSLog(@"mClass = %p",mClass);//元類對象指向的地址
    NSLog(@"mObjectClass = %p",mObjectClass);//NSObject元類對象指向的地址
    NSLog(@"mObjectClass = %p",mObjectClass2);//NSObject元類對象指向的地址
    
    struct lc_struct *aclass = (__bridge struct lc_struct *)(class);
    struct lc_struct *amClass = (__bridge struct lc_struct *)(mClass);
    struct lc_struct *amObjectClass = (__bridge struct lc_struct *)(mObjectClass);


2020-01-13 14:28:47.635802+0800 測試[9928:1196495] kvc = 0x6000010d1460
2020-01-13 14:28:47.635989+0800 測試[9928:1196495] class = 0x105c37a48
2020-01-13 14:28:47.636143+0800 測試[9928:1196495] mClass = 0x105c37a20
2020-01-13 14:28:47.636298+0800 測試[9928:1196495] mObjectClass = 0x7fff89cc4558
2020-01-13 14:28:47.636454+0800 測試[9928:1196495] mObjectClass = 0x7fff89cc4558


(lldb) p/x kvc 得到kvc對象的地址
(LCKVCtest *) $13 = 0x00006000010d1460
(lldb) p/x kvc->isa  //得到kvc結(jié)構(gòu)體中isa實例變量指向的地址
(Class) $14 = 0x0000000105c37a48 LCKVCtest
(lldb) p/x 0x0000000105c37a48 & 0x0000000ffffffff8ULL  // 對象中的isa& ISA_MASK得到類的地址
(unsigned long long) $15 = 0x0000000105c37a48
(lldb) p/x &(kvc->isa) 得到結(jié)構(gòu)體isa的地址(isa是結(jié)構(gòu)體中第一個成員變量所以也是結(jié)構(gòu)體的地址也就是對象的地址)
(__unsafe_unretained Class *) $16 = 0x00006000010d1460
(lldb) 

(lldb) p/x aclass->isa
(Class) $24 = 0x0000000105c37a20
(lldb) p/x 0x0000000105c37a20 & 0x0000000ffffffff8ULL
(unsigned long long) $25 = 0x0000000105c37a20
(lldb) p/x amClass->isa
(Class) $26 = 0x00007fff89cc4558
(lldb) p/x 0x00007fff89cc4558 & 0x0000000ffffffff8ULL
(unsigned long long) $27 = 0x0000000f89cc4558
(lldb) p/x amObjectClass->isa
(Class) $28 = 0x00007fff89cc4558
(lldb) p/x 0x00007fff89cc4558 & 0x0000000ffffffff8ULL
(unsigned long long) $29 = 0x0000000f89cc4558
(lldb) 
我們吧Class對象轉(zhuǎn)正我們自定的結(jié)構(gòu)體指針類對象中isa& ISA_MASK指向元類對象 元類對象的中的isa& ISA_MASK指向NSObejc元類,NSObejc元類中的isa指向自己. (不用& ISA_MASK)

描述對象類和元類的關(guān)系這里來個張神圖鎮(zhèn)場再合適不過了


image.png

objc_class的布局詳細說明每個變量代表的含義

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

} OBJC2_UNAVAILABLE;
這里是類對象的內(nèi)存布局 但是遺憾的是!__OBJC2__ 已經(jīng)不是現(xiàn)在的版本了.
現(xiàn)在的版本是__OBJC2__ 
并且OBJC2_UNAVAILABLE 說明在__OBJC2__版本下這個結(jié)構(gòu)體也是不可用的,我們可以簡單分析一下

isa 可以簡單的人為是指向元類(其實需要$ISA_MASK)
super_class 指向父類
ivars 是成員變量列表
objc_method_list 方法列表
cache 方法緩存
protocols 協(xié)議類表

現(xiàn)在的class 結(jié)構(gòu)如下

//對象結(jié)構(gòu)體
struct objc_class : objc_object {
    // Class ISA;
    Class superclass;
    cache_t cache;             // formerly cache pointer and vtable
    class_data_bits_t bits;    // class_rw_t * plus custom rr/alloc flags

    class_rw_t *data() { 
        return bits.data();
    }
    void setData(class_rw_t *newData) {
        bits.setData(newData);
    }

    void setInfo(uint32_t set) {
        assert(isFuture()  ||  isRealized());
        data()->setFlags(set);
    }

    void clearInfo(uint32_t clear) {
        assert(isFuture()  ||  isRealized());
        data()->clearFlags(clear);
    }

    // set and clear must not overlap
    void changeInfo(uint32_t set, uint32_t clear) {
        assert(isFuture()  ||  isRealized());
        assert((set & clear) == 0);
        data()->changeFlags(set, clear);
    }

    bool hasCustomRR() {
        return ! bits.hasDefaultRR();
    }
    void setHasDefaultRR() {
        assert(isInitializing());
        bits.setHasDefaultRR();
    }
    void setHasCustomRR(bool inherited = false);
    void printCustomRR(bool inherited);

    bool hasCustomAWZ() {
        return ! bits.hasDefaultAWZ();
    }
    void setHasDefaultAWZ() {
        assert(isInitializing());
        bits.setHasDefaultAWZ();
    }
    void setHasCustomAWZ(bool inherited = false);
    void printCustomAWZ(bool inherited);

    bool instancesRequireRawIsa() {
        return bits.instancesRequireRawIsa();
    }
    void setInstancesRequireRawIsa(bool inherited = false);
    void printInstancesRequireRawIsa(bool inherited);

    bool canAllocNonpointer() {
        assert(!isFuture());
        return !instancesRequireRawIsa();
    }
    bool canAllocFast() {
        assert(!isFuture());
        return bits.canAllocFast();
    }


    bool hasCxxCtor() {
        // addSubclass() propagates this flag from the superclass.
        assert(isRealized());
        return bits.hasCxxCtor();
    }
    void setHasCxxCtor() { 
        bits.setHasCxxCtor();
    }

    bool hasCxxDtor() {
        // addSubclass() propagates this flag from the superclass.
        assert(isRealized());
        return bits.hasCxxDtor();
    }
    void setHasCxxDtor() { 
        bits.setHasCxxDtor();
    }


    bool isSwift() {
        return bits.isSwift();
    }


    // Return YES if the class's ivars are managed by ARC, 
    // or the class is MRC but has ARC-style weak ivars.
    bool hasAutomaticIvars() {
        return data()->ro->flags & (RO_IS_ARC | RO_HAS_WEAK_WITHOUT_ARC);
    }

    // Return YES if the class's ivars are managed by ARC.
    bool isARC() {
        return data()->ro->flags & RO_IS_ARC;
    }


#if SUPPORT_NONPOINTER_ISA
    // Tracked in non-pointer isas; not tracked otherwise
#else
    bool instancesHaveAssociatedObjects() {
        // this may be an unrealized future class in the CF-bridged case
        assert(isFuture()  ||  isRealized());
        return data()->flags & RW_INSTANCES_HAVE_ASSOCIATED_OBJECTS;
    }

    void setInstancesHaveAssociatedObjects() {
        // this may be an unrealized future class in the CF-bridged case
        assert(isFuture()  ||  isRealized());
        setInfo(RW_INSTANCES_HAVE_ASSOCIATED_OBJECTS);
    }
#endif

    bool shouldGrowCache() {
        return true;
    }

    void setShouldGrowCache(bool) {
        // fixme good or bad for memory use?
    }

    bool isInitializing() {
        return getMeta()->data()->flags & RW_INITIALIZING;
    }

    void setInitializing() {
        assert(!isMetaClass());
        ISA()->setInfo(RW_INITIALIZING);
    }

    bool isInitialized() {
        return getMeta()->data()->flags & RW_INITIALIZED;
    }

    void setInitialized();

    bool isLoadable() {
        assert(isRealized());
        return true;  // any class registered for +load is definitely loadable
    }

    IMP getLoadMethod();

    // Locking: To prevent concurrent realization, hold runtimeLock.
    bool isRealized() {
        return data()->flags & RW_REALIZED;
    }

    // Returns true if this is an unrealized future class.
    // Locking: To prevent concurrent realization, hold runtimeLock.
    bool isFuture() { 
        return data()->flags & RW_FUTURE;
    }

    bool isMetaClass() {
        assert(this);
        assert(isRealized());
        return data()->ro->flags & RO_META;
    }

    // NOT identical to this->ISA when this is a metaclass
    Class getMeta() {
        if (isMetaClass()) return (Class)this;
        else return this->ISA();
    }

    bool isRootClass() {
        return superclass == nil;
    }
    bool isRootMetaclass() {
        return ISA() == (Class)this;
    }

    const char *mangledName() { 
        // fixme can't assert locks here
        assert(this);

        if (isRealized()  ||  isFuture()) {
            return data()->ro->name;
        } else {
            return ((const class_ro_t *)data())->name;
        }
    }
    
    const char *demangledName(bool realize = false);
    const char *nameForLogging();

    // May be unaligned depending on class's ivars.
    uint32_t unalignedInstanceStart() {
        assert(isRealized());
        return data()->ro->instanceStart;
    }

    // Class's instance start rounded up to a pointer-size boundary.
    // This is used for ARC layout bitmaps.
    uint32_t alignedInstanceStart() {
        return word_align(unalignedInstanceStart());
    }

    // May be unaligned depending on class's ivars.
    uint32_t unalignedInstanceSize() {
        assert(isRealized());
        return data()->ro->instanceSize;
    }

    // Class's ivar size rounded up to a pointer-size boundary.
    uint32_t alignedInstanceSize() {
        return word_align(unalignedInstanceSize());
    }

    size_t instanceSize(size_t extraBytes) {
        size_t size = alignedInstanceSize() + extraBytes;
        // CF requires all objects be at least 16 bytes.
        if (size < 16) size = 16;
        return size;
    }

    void setInstanceSize(uint32_t newSize) {
        assert(isRealized());
        if (newSize != data()->ro->instanceSize) {
            assert(data()->flags & RW_COPIED_RO);
            *const_cast<uint32_t *>(&data()->ro->instanceSize) = newSize;
        }
        bits.setFastInstanceSize(newSize);
    }

    void chooseClassArrayIndex();

    void setClassArrayIndex(unsigned Idx) {
        bits.setClassArrayIndex(Idx);
    }

    unsigned classArrayIndex() {
        return bits.classArrayIndex();
    }
}



//class_rw_t 結(jié)構(gòu)體
struct class_rw_t {
    // Be warned that Symbolication knows the layout of this structure.
    uint32_t flags;
    uint32_t version;

    const class_ro_t *ro;

    method_array_t methods;
    property_array_t properties;
    protocol_array_t protocols;

    Class firstSubclass;
    Class nextSiblingClass;

    char *demangledName;

#if SUPPORT_INDEXED_ISA
    uint32_t index;
#endif

    void setFlags(uint32_t set) 
    {
        OSAtomicOr32Barrier(set, &flags);
    }

    void clearFlags(uint32_t clear) 
    {
        OSAtomicXor32Barrier(clear, &flags);
    }

    // set and clear must not overlap
    void changeFlags(uint32_t set, uint32_t clear) 
    {
        assert((set & clear) == 0);

        uint32_t oldf, newf;
        do {
            oldf = flags;
            newf = (oldf | set) & ~clear;
        } while (!OSAtomicCompareAndSwap32Barrier(oldf, newf, (volatile int32_t *)&flags));
    }
};


struct class_data_bits_t {

    // Values are the FAST_ flags above.
    uintptr_t bits;
private:
    bool getBit(uintptr_t bit)
    {
        return bits & bit;
    }

#if FAST_ALLOC
    static uintptr_t updateFastAlloc(uintptr_t oldBits, uintptr_t change)
    {
        if (change & FAST_ALLOC_MASK) {
            if (((oldBits & FAST_ALLOC_MASK) == FAST_ALLOC_VALUE)  &&  
                ((oldBits >> FAST_SHIFTED_SIZE_SHIFT) != 0)) 
            {
                oldBits |= FAST_ALLOC;
            } else {
                oldBits &= ~FAST_ALLOC;
            }
        }
        return oldBits;
    }
#else
    static uintptr_t updateFastAlloc(uintptr_t oldBits, uintptr_t change) {
        return oldBits;
    }
#endif

    void setBits(uintptr_t set) 
    {
        uintptr_t oldBits;
        uintptr_t newBits;
        do {
            oldBits = LoadExclusive(&bits);
            newBits = updateFastAlloc(oldBits | set, set);
        } while (!StoreReleaseExclusive(&bits, oldBits, newBits));
    }

    void clearBits(uintptr_t clear) 
    {
        uintptr_t oldBits;
        uintptr_t newBits;
        do {
            oldBits = LoadExclusive(&bits);
            newBits = updateFastAlloc(oldBits & ~clear, clear);
        } while (!StoreReleaseExclusive(&bits, oldBits, newBits));
    }

public:

    class_rw_t* data() {
        return (class_rw_t *)(bits & FAST_DATA_MASK);
    }
    void setData(class_rw_t *newData)
    {
        assert(!data()  ||  (newData->flags & (RW_REALIZING | RW_FUTURE)));
        // Set during realization or construction only. No locking needed.
        // Use a store-release fence because there may be concurrent
        // readers of data and data's contents.
        uintptr_t newBits = (bits & ~FAST_DATA_MASK) | (uintptr_t)newData;
        atomic_thread_fence(memory_order_release);
        bits = newBits;
    }

    bool hasDefaultRR() {
        return getBit(FAST_HAS_DEFAULT_RR);
    }
    void setHasDefaultRR() {
        setBits(FAST_HAS_DEFAULT_RR);
    }
    void setHasCustomRR() {
        clearBits(FAST_HAS_DEFAULT_RR);
    }

#if FAST_HAS_DEFAULT_AWZ
    bool hasDefaultAWZ() {
        return getBit(FAST_HAS_DEFAULT_AWZ);
    }
    void setHasDefaultAWZ() {
        setBits(FAST_HAS_DEFAULT_AWZ);
    }
    void setHasCustomAWZ() {
        clearBits(FAST_HAS_DEFAULT_AWZ);
    }
#else
    bool hasDefaultAWZ() {
        return data()->flags & RW_HAS_DEFAULT_AWZ;
    }
    void setHasDefaultAWZ() {
        data()->setFlags(RW_HAS_DEFAULT_AWZ);
    }
    void setHasCustomAWZ() {
        data()->clearFlags(RW_HAS_DEFAULT_AWZ);
    }
#endif

#if FAST_HAS_CXX_CTOR
    bool hasCxxCtor() {
        return getBit(FAST_HAS_CXX_CTOR);
    }
    void setHasCxxCtor() {
        setBits(FAST_HAS_CXX_CTOR);
    }
#else
    bool hasCxxCtor() {
        return data()->flags & RW_HAS_CXX_CTOR;
    }
    void setHasCxxCtor() {
        data()->setFlags(RW_HAS_CXX_CTOR);
    }
#endif

#if FAST_HAS_CXX_DTOR
    bool hasCxxDtor() {
        return getBit(FAST_HAS_CXX_DTOR);
    }
    void setHasCxxDtor() {
        setBits(FAST_HAS_CXX_DTOR);
    }
#else
    bool hasCxxDtor() {
        return data()->flags & RW_HAS_CXX_DTOR;
    }
    void setHasCxxDtor() {
        data()->setFlags(RW_HAS_CXX_DTOR);
    }
#endif

#if FAST_REQUIRES_RAW_ISA
    bool instancesRequireRawIsa() {
        return getBit(FAST_REQUIRES_RAW_ISA);
    }
    void setInstancesRequireRawIsa() {
        setBits(FAST_REQUIRES_RAW_ISA);
    }
#elif SUPPORT_NONPOINTER_ISA
    bool instancesRequireRawIsa() {
        return data()->flags & RW_REQUIRES_RAW_ISA;
    }
    void setInstancesRequireRawIsa() {
        data()->setFlags(RW_REQUIRES_RAW_ISA);
    }
#else
    bool instancesRequireRawIsa() {
        return true;
    }
    void setInstancesRequireRawIsa() {
        // nothing
    }
#endif

#if FAST_ALLOC
    size_t fastInstanceSize() 
    {
        assert(bits & FAST_ALLOC);
        return (bits >> FAST_SHIFTED_SIZE_SHIFT) * 16;
    }
    void setFastInstanceSize(size_t newSize) 
    {
        // Set during realization or construction only. No locking needed.
        assert(data()->flags & RW_REALIZING);

        // Round up to 16-byte boundary, then divide to get 16-byte units
        newSize = ((newSize + 15) & ~15) / 16;
        
        uintptr_t newBits = newSize << FAST_SHIFTED_SIZE_SHIFT;
        if ((newBits >> FAST_SHIFTED_SIZE_SHIFT) == newSize) {
            int shift = WORD_BITS - FAST_SHIFTED_SIZE_SHIFT;
            uintptr_t oldBits = (bits << shift) >> shift;
            if ((oldBits & FAST_ALLOC_MASK) == FAST_ALLOC_VALUE) {
                newBits |= FAST_ALLOC;
            }
            bits = oldBits | newBits;
        }
    }

    bool canAllocFast() {
        return bits & FAST_ALLOC;
    }
#else
    size_t fastInstanceSize() {
        abort();
    }
    void setFastInstanceSize(size_t) {
        // nothing
    }
    bool canAllocFast() {
        return false;
    }
#endif

    void setClassArrayIndex(unsigned Idx) {
#if SUPPORT_INDEXED_ISA
        // 0 is unused as then we can rely on zero-initialisation from calloc.
        assert(Idx > 0);
        data()->index = Idx;
#endif
    }

    unsigned classArrayIndex() {
#if SUPPORT_INDEXED_ISA
        return data()->index;
#else
        return 0;
#endif
    }

    bool isSwift() {
        return getBit(FAST_IS_SWIFT);
    }

    void setIsSwift() {
        setBits(FAST_IS_SWIFT);
    }
}

//cache
struct cache_t {
    struct bucket_t *_buckets;
    mask_t _mask;
    mask_t _occupied;

public:
    struct bucket_t *buckets();
    mask_t mask();
    mask_t occupied();
    void incrementOccupied();
    void setBucketsAndMask(struct bucket_t *newBuckets, mask_t newMask);
    void initializeToEmpty();

    mask_t capacity();
    bool isConstantEmptyCache();
    bool canBeFreed();

    static size_t bytesForCapacity(uint32_t cap);
    static struct bucket_t * endMarker(struct bucket_t *b, uint32_t cap);

    void expand();
    void reallocate(mask_t oldCapacity, mask_t newCapacity);
    struct bucket_t * find(cache_key_t key, id receiver);

    static void bad_cache(id receiver, SEL sel, Class isa) __attribute__((noreturn));
};


以上是現(xiàn)在objc_class現(xiàn)在的內(nèi)存布局 我們摘出存在objc_class結(jié)構(gòu)體內(nèi)存里面的關(guān)鍵信息

struct objc_object {
private:
    isa_t isa;

public:

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

    // getIsa() allows this to be a tagged pointer object
    Class getIsa();
......
}

在arm64架構(gòu)之前,isa就是一個普通的指針,存儲著Class、Meta-Class對象的內(nèi)存地址
從arm64架構(gòu)開始,對isa進行了優(yōu)化,變成了一個共用體(union)結(jié)構(gòu),還使用位域來存儲更多的信息

image.png

nonpointer
0,代表普通的指針,存儲著Class、Meta-Class對象的內(nèi)存地址
1,代表優(yōu)化過,使用位域存儲更多的信息

has_assoc
是否有設(shè)置過關(guān)聯(lián)對象,如果沒有,釋放時會更快

has_cxx_dtor
是否有C++的析構(gòu)函數(shù)(.cxx_destruct),如果沒有,釋放時會更快

shiftcls
存儲著Class、Meta-Class對象的內(nèi)存地址信息

magic
用于在調(diào)試時分辨對象是否未完成初始化

weakly_referenced
是否有被弱引用指向過,如果沒有,釋放時會更快

deallocating
對象是否正在釋放

extra_rc
里面存儲的值是引用計數(shù)器減1

has_sidetable_rc
引用計數(shù)器是否過大無法存儲在isa中
如果為1,那么引用計數(shù)會存儲在一個叫SideTable的類的屬性中

分類底層的實現(xiàn)

分類在runtime中結(jié)構(gòu)是這樣的我們每一個分類 都會生成一個這樣的結(jié)構(gòu)體
下面是分類在objc代碼源碼中的結(jié)構(gòu)體

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;
    // Fields below this point are not always present on disk.
    struct property_list_t *_classProperties;

    method_list_t *methodsForMeta(bool isMeta) {
        if (isMeta) return classMethods;
        else return instanceMethods;
    }

    property_list_t *propertiesForMeta(bool isMeta, struct header_info *hi);
};

#import "LCTest.h"

NS_ASSUME_NONNULL_BEGIN

@interface LCTest (aaaa)<NSCopying>

@property (nonatomic, strong) NSString *aaStr;

+ (void)aaajia;

- (void)aaajian;

@end

NS_ASSUME_NONNULL_END


#import "LCTest+aaaa.h"

@implementation LCTest (aaaa)

+ (void)aaajia{
    
}

- (void)aaajian{
    
}

- (id)copyWithZone:(NSZone *)zone{
    return nil;
}

@end

我們用xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc LCTest+aaaa.m 看看分類轉(zhuǎn)換成的c++代碼

static struct _category_t _OBJC_$_CATEGORY_LCTest_$_aaaa __attribute__ ((used, section ("__DATA,__objc_const"))) = 
{
    "LCTest",
    0, // &OBJC_CLASS_$_LCTest,
    (const struct _method_list_t *)&_OBJC_$_CATEGORY_INSTANCE_METHODS_LCTest_$_aaaa,
    (const struct _method_list_t *)&_OBJC_$_CATEGORY_CLASS_METHODS_LCTest_$_aaaa,
    (const struct _protocol_list_t *)&_OBJC_CATEGORY_PROTOCOLS_$_LCTest_$_aaaa,
    (const struct _prop_list_t *)&_OBJC_$_PROP_LIST_LCTest_$_aaaa,
};

//這是傳換成的c++基本和runtime源碼中的一致
struct _category_t {
    const char *name;
    struct _class_t *cls;
    const struct _method_list_t *instance_methods;
    const struct _method_list_t *class_methods;
    const struct _protocol_list_t *protocols;
    const struct _prop_list_t *properties;
};

//存放實例方法的_method_list_t
static struct /*_method_list_t*/ {
    unsigned int entsize;  // sizeof(struct _objc_method)
    unsigned int method_count;
    struct _objc_method method_list[2];
} _OBJC_$_CATEGORY_INSTANCE_METHODS_LCTest_$_aaaa __attribute__ ((used, section ("__DATA,__objc_const"))) = {
    sizeof(_objc_method),
    2,
    {{(struct objc_selector *)"aaajian", "v16@0:8", (void *)_I_LCTest_aaaa_aaajian},
    {(struct objc_selector *)"copyWithZone:", "@24@0:8^{_NSZone=}16", (void *)_I_LCTest_aaaa_copyWithZone_}}
};

//存放類方法的_method_list_t
static struct /*_method_list_t*/ {
    unsigned int entsize;  // sizeof(struct _objc_method)
    unsigned int method_count;
    struct _objc_method method_list[1];
} _OBJC_$_CATEGORY_CLASS_METHODS_LCTest_$_aaaa __attribute__ ((used, section ("__DATA,__objc_const"))) = {
    sizeof(_objc_method),
    1,
    {{(struct objc_selector *)"aaajia", "v16@0:8", (void *)_C_LCTest_aaaa_aaajia}}
};

static struct /*_protocol_list_t*/ {
    long protocol_count;  // Note, this is 32/64 bit
    struct _protocol_t *super_protocols[1];
} _OBJC_CATEGORY_PROTOCOLS_$_LCTest_$_aaaa __attribute__ ((used, section ("__DATA,__objc_const"))) = {
    1,
    &_OBJC_PROTOCOL_NSCopying
};

//關(guān)于協(xié)議在分類中
struct _protocol_t {
    void * isa;  // NULL
    const char *protocol_name;
    const struct _protocol_list_t * protocol_list; // super protocols
    const struct method_list_t *instance_methods;
    const struct method_list_t *class_methods;
    const struct method_list_t *optionalInstanceMethods;
    const struct method_list_t *optionalClassMethods;
    const struct _prop_list_t * properties;
    const unsigned int size;  // sizeof(struct _protocol_t)
    const unsigned int flags;  // = 0
    const char ** extendedMethodTypes;
};
struct _protocol_t _OBJC_PROTOCOL_NSCopying __attribute__ ((used)) = {
    0,
    "NSCopying",
    0,
    (const struct method_list_t *)&_OBJC_PROTOCOL_INSTANCE_METHODS_NSCopying,
    0,
    0,
    0,
    0,
    sizeof(_protocol_t),
    0,
    (const char **)&_OBJC_PROTOCOL_METHOD_TYPES_NSCopying
};

//屬性列表
static struct /*_prop_list_t*/ {
    unsigned int entsize;  // sizeof(struct _prop_t)
    unsigned int count_of_properties;
    struct _prop_t prop_list[1];
} _OBJC_$_PROP_LIST_LCTest_$_aaaa __attribute__ ((used, section ("__DATA,__objc_const"))) = {
    sizeof(_prop_t),
    1,
    {{"aaStr","T@\"NSString\",&,N"}}
};

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

我們從上面可以看到一個類的一個分類底層其實是一個結(jié)構(gòu)體.
結(jié)構(gòu)體中包含的有
1類的 名字
2..
3實例方法列表
4對象方法類表
5協(xié)議類表
6屬性列表

我們知道一個對象調(diào)用一個實例方法尋找這個方法其實是在類里面的尋找的,如果我們調(diào)用一個分類的實例方法,是怎么找到這個方法的呢?

我們根據(jù)runtime的源碼分析一下
源碼解讀順序
objc-os.mm
_objc_init
map_images
map_images_nolock

objc-runtime-new.mm
_read_images
remethodizeClass
attachCategories
attachLists
realloc、memmove、 memcpy

// 一行一行分析代碼難度比較大我們直接分析attachCategories 這個方法

static void 
attachCategories(Class cls, category_list *cats, bool flush_caches)
{
    if (!cats) return;
    if (PrintReplacedMethods) printReplacements(cls, cats);

    bool isMeta = cls->isMetaClass();

    // fixme rearrange to remove these intermediate allocations
    method_list_t **mlists = (method_list_t **)
        malloc(cats->count * sizeof(*mlists));
    property_list_t **proplists = (property_list_t **)
        malloc(cats->count * sizeof(*proplists));
    protocol_list_t **protolists = (protocol_list_t **)
        malloc(cats->count * sizeof(*protolists));

    // Count backwards through cats to get newest categories first
    int mcount = 0;
    int propcount = 0;
    int protocount = 0;
    int i = cats->count; // i 是分類的個數(shù)
    bool fromBundle = NO;
    while (i--) {
        auto& entry = cats->list[I];

        method_list_t *mlist = entry.cat->methodsForMeta(isMeta);//我看可以看到因為i--所以所以取得是最后一個分類的方法類表 根據(jù)isMeta判斷取得是 實例方法還是對象方法
        if (mlist) {
            mlists[mcount++] = mlist;// 我們?nèi)〉降姆椒惐?放到mlists這個二維數(shù)組中
            fromBundle |= entry.hi->isBundle();
        }

        //下面屬性 和協(xié)議和上面類似

        property_list_t *proplist = 
            entry.cat->propertiesForMeta(isMeta, entry.hi);
        if (proplist) {
            proplists[propcount++] = proplist;
        }

        protocol_list_t *protolist = entry.cat->protocols;
        if (protolist) {
            protolists[protocount++] = protolist;
        }
    }

    auto rw = cls->data(); //這里 auto tw 就是 class_rw_t *rw 我們知道cla->data返回的就是這個結(jié)構(gòu)體指針 上面分析objc_class的源碼中可以看到

    prepareMethodLists(cls, mlists, mcount, NO, fromBundle);
    rw->methods.attachLists(mlists, mcount); //我們重點分析一下這個方法
    free(mlists);
    if (flush_caches  &&  mcount > 0) flushCaches(cls);

    rw->properties.attachLists(proplists, propcount);
    free(proplists);

    rw->protocols.attachLists(protolists, protocount);
    free(protolists);
}

//這是關(guān)鍵代碼上下文略去了 
void attachLists(List* const * addedLists, uint32_t addedCount) {
        if (addedCount == 0) return;

        if (hasArray()) { //hasArray()這個方法表示自己是否有元素也就是 rw->methods是否為空 (如果一個類本身沒有任何實例方法,和有自己的實例方法 在合并分類里面的實例方法根據(jù)這個條件分情況考慮)
            // many lists -> many lists
            uint32_t oldCount = array()->count; //數(shù)組原來的長度
            uint32_t newCount = oldCount + addedCount; //加上所有分類后的長度
            setArray((array_t *)realloc(array(), array_t::byteSize(newCount))); 分配新的空間
            array()->count = newCount;
            
            // 從array()->lists指向的那塊內(nèi)存開始拷貝 拷貝長度為oldCount 拷貝到array()->lists + addedCount起始的地址 (array()->lists的內(nèi)容移動了 但是array()->lists指向還沒變)
            memmove(array()->lists + addedCount,
                    array()->lists,
                    oldCount * sizeof(array()->lists[0]));
            // 從addedLists指向的那塊內(nèi)存開始拷貝 拷貝長度為addedCount 拷貝到array()->lists,t起始的地址
            memcpy(array()->lists,
                   addedLists, 
                   addedCount * sizeof(array()->lists[0]));  
            //上面的兩個方法其實是 吧我們分類的方法插到到了類方法前面
        }
        else if (!list  &&  addedCount == 1) {
            // 0 lists -> 1 list
            list = addedLists[0];
        } 
        else {
            // 1 list -> many lists
            List* oldList = list;
            uint32_t oldCount = oldList ? 1 : 0;
            uint32_t newCount = oldCount + addedCount;
            setArray((array_t *)malloc(array_t::byteSize(newCount)));
            array()->count = newCount;
            if (oldList) array()->lists[addedCount] = oldList;
            memcpy(array()->lists, addedLists, 
                   addedCount * sizeof(array()->lists[0]));
        }
    }


這個方法是吧一個類的所有分類里面的,方法,協(xié)議,屬性,存到類里面

method_list_t **mlists = (method_list_t *)malloc(cats->count * sizeof(mlists));

property_list_t **proplists = (property_list_t *)malloc(cats->count * sizeof(proplists));

protocol_list_t **protolists = (protocol_list_t *)malloc(cats->count * sizeof(protolists));
首先根據(jù)分類的個數(shù)通過malloc分配三個 二維數(shù)組. 我們通過分析method_list_t * * mlists存到東西來分析一下為什么是二維數(shù)組
[
[數(shù)組1里面存的是ClassA_ClassA_category_test1的所有對象方法],
[數(shù)組2里面存的是ClassA_ClassA_category_test2的所有對象方法],
[數(shù)組3里面存的是ClassA_ClassA_category_test2的所有對象方法]
]
一個類可以有 很多很多分類 一個分類可以有 很多對象方法

我們重點分析一下 while看上面代碼注釋
經(jīng)過上面的分析我們 可以得出下面的結(jié)論
分類其實就是一個結(jié)構(gòu)體 包含了,方法列表,屬性列表,協(xié)議列表
進過runtime會吧分類里面的信息插入到類的前面

介紹你知道的關(guān)于所有l(wèi)oad方法所有的知識點

(1)load方法的調(diào)用時機
(2)load調(diào)用方法的調(diào)用順序
load方法的調(diào)用時機是在runtime初始化的時候調(diào)用的,在main函數(shù)前面,我們分析load方法還是從源碼分析入手

//runtime初始化入口
void _objc_init(void)
{
    static bool initialized = false;
    if (initialized) return;
    initialized = true;
    
    // fixme defer initialization until an objc-using image is found?
    environ_init();
    tls_init();
    static_init();
    lock_init();
    exception_init();

    _dyld_objc_notify_register(&map_images, load_images, unmap_image);
}
//load_images是作為方法指針 我們看一下load_images方法

load_images(const char *path __unused, const struct mach_header *mh)
{
    // Return without taking locks if there are no +load methods here.
    if (!hasLoadMethods((const headerType *)mh)) return;

    recursive_mutex_locker_t lock(loadMethodLock);

    // Discover load methods
    {
        rwlock_writer_t lock2(runtimeLock);
        prepare_load_methods((const headerType *)mh);
    }

    // Call +load methods (without runtimeLock - re-entrant)
    call_load_methods();
}
這里面有兩個要看的方法 1是prepare_load_methods 2是call_load_methods

//這個方法做了兩件事情 1是安排所有類  2便利所有分類生成一個結(jié)構(gòu)體數(shù)組
void prepare_load_methods(const headerType *mhdr)
{
    size_t count, I;

    runtimeLock.assertWriting();

    classref_t *classlist = 
        _getObjc2NonlazyClassList(mhdr, &count);
    for (i = 0; i < count; i++) {
        schedule_class_load(remapClass(classlist[i]));
    }

    category_t **categorylist = _getObjc2NonlazyCategoryList(mhdr, &count);
    for (i = 0; i < count; i++) {
        category_t *cat = categorylist[I];
        Class cls = remapClass(cat->cls);
        if (!cls) continue;  // category for ignored weak-linked class
        realizeClass(cls);
        assert(cls->ISA()->isRealized());
        add_category_to_loadable_list(cat);
    }
}

我們再看 schedule_class_load 和 add_category_to_loadable_list怎么吧
重點來了
我們看到 schedule_class_load 會遞歸的找一個類的父類, 
調(diào)用add_class_to_loadable_list,如果在這個方法中會判斷類中是否實現(xiàn)了+load方法如果實現(xiàn)了就會吧 這個類和+load方法作為成員變量存在一個結(jié)構(gòu)體數(shù)組中. 這樣就生成了一個存著 類,和這個類對應(yīng)的load方法的結(jié)構(gòu)體數(shù)組 loadable_classes
static void schedule_class_load(Class cls)
{
    if (!cls) return;
    assert(cls->isRealized());  // _read_images should realize

    if (cls->data()->flags & RW_LOADED) return;

    // Ensure superclass-first ordering
    schedule_class_load(cls->superclass);

    add_class_to_loadable_list(cls);
    cls->setInfo(RW_LOADED); 
}
void add_class_to_loadable_list(Class cls)
{
    IMP method;

    loadMethodLock.assertLocked();

    method = cls->getLoadMethod();
    if (!method) return;  // Don't bother if cls has no +load method
    
    if (PrintLoading) {
        _objc_inform("LOAD: class '%s' scheduled for +load", 
                     cls->nameForLogging());
    }
    
    if (loadable_classes_used == loadable_classes_allocated) {
        loadable_classes_allocated = loadable_classes_allocated*2 + 16;
        loadable_classes = (struct loadable_class *)
            realloc(loadable_classes,
                              loadable_classes_allocated *
                              sizeof(struct loadable_class));
    }
    
    loadable_classes[loadable_classes_used].cls = cls;
    loadable_classes[loadable_classes_used].method = method;
    loadable_classes_used++;
}

//add_category_to_loadable_list和上面差不多 最后會有一個loadable_categories一個結(jié)構(gòu)體數(shù)組
void add_category_to_loadable_list(Category cat)
{
    IMP method;

    loadMethodLock.assertLocked();

    method = _category_getLoadMethod(cat);

    // Don't bother if cat has no +load method
    if (!method) return;

    if (PrintLoading) {
        _objc_inform("LOAD: category '%s(%s)' scheduled for +load", 
                     _category_getClassName(cat), _category_getName(cat));
    }
    
    if (loadable_categories_used == loadable_categories_allocated) {
        loadable_categories_allocated = loadable_categories_allocated*2 + 16;
        loadable_categories = (struct loadable_category *)
            realloc(loadable_categories,
                              loadable_categories_allocated *
                              sizeof(struct loadable_category));
    }

    loadable_categories[loadable_categories_used].cat = cat;
    loadable_categories[loadable_categories_used].method = method;
    loadable_categories_used++;
}

//下面是結(jié)構(gòu)體的結(jié)構(gòu)
struct loadable_class {
    Class cls;  // may be nil
    IMP method;
};

struct loadable_category {
    Category cat;  // may be nil
    IMP method;
};

總結(jié):經(jīng)過上面的源碼分析我們發(fā)現(xiàn) runtime初始化期間會 生成一個loadable_class數(shù)組結(jié)構(gòu)體并且是按照加載類的順序,并且父類在前面子類在后的順序 和loadable_category數(shù)組結(jié)構(gòu)體順序和加載分類的順序相同

下面我們在分析剩下的部分直到找到+load是怎么自動調(diào)動的揭開+load神秘的面紗 我們接著分析call_load_methods方法

void call_load_methods(void)
{
    static bool loading = NO;
    bool more_categories;

    loadMethodLock.assertLocked();

    // Re-entrant calls do nothing; the outermost call will finish the job.
    if (loading) return;
    loading = YES;

    void *pool = objc_autoreleasePoolPush();

    do {
        // 1. Repeatedly call class +loads until there aren't any more
        while (loadable_classes_used > 0) {
            call_class_loads();
        }

        // 2. Call category +loads ONCE
        more_categories = call_category_loads();

        // 3. Run more +loads if there are classes OR more untried categories
    } while (loadable_classes_used > 0  ||  more_categories);

    objc_autoreleasePoolPop(pool);

    loading = NO;
}
 loadable_classes_used 這個值就是loadable_class數(shù)組的長度

從方法名字可以看出
call_class_loads //調(diào)用類的load方法
call_category_loads //調(diào)用分類的load方法
我們接下來分析一下這兩個方法

static void call_class_loads(void)
{
    int I;
    
    // Detach current loadable list.
    struct loadable_class *classes = loadable_classes;
    int used = loadable_classes_used; //loadable_classes數(shù)組的長度
    loadable_classes = nil;
    loadable_classes_allocated = 0;
    loadable_classes_used = 0; //賦值成0為了結(jié)束while (loadable_classes_used > 0) {...}循環(huán)
        

    
    // Call all +loads for the detached list.
    for (i = 0; i < used; i++) {
       //我們還記得loadable_class結(jié)構(gòu)體的結(jié)構(gòu)吧 里面有+load方法的實現(xiàn) 有類
        Class cls = classes[i].cls; //取出結(jié)構(gòu)體里面的類
        load_method_t load_method = (load_method_t)classes[i].method; //取出結(jié)構(gòu)體里面的load方法指針
        if (!cls) continue; 

        if (PrintLoading) {
            _objc_inform("LOAD: +[%s load]\n", cls->nameForLogging());
        }
        (*load_method)(cls, SEL_load); //用load函數(shù)指針直接調(diào)用.沒有走sendMessage()這一套流程
    }
    
    // Destroy the detached list.
    if (classes) free(classes);
}
static bool call_category_loads(void)
{
    int i, shift;
    bool new_categories_added = NO;
    
    // Detach current loadable list.
    struct loadable_category *cats = loadable_categories;
    int used = loadable_categories_used;
    int allocated = loadable_categories_allocated;
    loadable_categories = nil;
    loadable_categories_allocated = 0;
    loadable_categories_used = 0;

    // Call all +loads for the detached list.
    for (i = 0; i < used; i++) {
        Category cat = cats[i].cat;
        load_method_t load_method = (load_method_t)cats[i].method;
        Class cls;
        if (!cat) continue;

        cls = _category_getClass(cat);
        if (cls  &&  cls->isLoadable()) {
            if (PrintLoading) {
                _objc_inform("LOAD: +[%s(%s) load]\n", 
                             cls->nameForLogging(), 
                             _category_getName(cat));
            }
            (*load_method)(cls, SEL_load);
            cats[i].cat = nil;
        }
    }
  ... 
}
//我們看出調(diào)用分類的load方法 和調(diào)用類里面的load方法差不都 也是便利拿出 loadable_categorie 結(jié)構(gòu)體再取出 分類通過分類取出類, 再拿出load方法直接通過 結(jié)構(gòu)體中存的load函數(shù)指針調(diào)用load方法.

到這里load的源碼部分基本分析完了,其實從頭開始看也不難,我現(xiàn)在總結(jié)一下
總結(jié)
1如果有類實現(xiàn)了+load方法 先加載類的+load方法不管編譯順序誰前誰后
2加載類的+load方法和類的加載順序有關(guān)系 誰在前面先調(diào)用誰但是在調(diào)用類的+load方法之前會遞歸的判斷父類是否實現(xiàn)了+load方法父類實現(xiàn)了就先調(diào)用父類的+load方法
3分類的+load和分類的加載順序一樣
4調(diào)用+load的過程是在runtime初始化的時候會先便利所有類便利順序和類的加載順序一樣 判斷類是否實現(xiàn)了+load方法如果yes就用遞歸去便利父類.這樣吧所有實現(xiàn)load方法的 類和load函數(shù)指針存在結(jié)構(gòu)體中 分類同理 在調(diào)用的+load方法時再便利結(jié)構(gòu)體數(shù)組通過+load函數(shù)指針直接調(diào)用 分類同理

initialize

1調(diào)用時機
2調(diào)用過程
+initialize方法會在類第一次接收到消息時調(diào)用 所以分析一下類接受消息的的一個過程 關(guān)鍵代碼在 查找類里面消息這一步

IMP lookUpImpOrForward(Class cls, SEL sel, id inst, 
                       bool initialize, bool cache, bool resolver)
{
    IMP imp = nil;
    bool triedResolver = NO;

    runtimeLock.assertUnlocked();

    // Optimistic cache lookup
    if (cache) {
        imp = cache_getImp(cls, sel);
        if (imp) return imp;
    }

    // runtimeLock is held during isRealized and isInitialized checking
    // to prevent races against concurrent realization.

    // runtimeLock is held during method search to make
    // method-lookup + cache-fill atomic with respect to method addition.
    // Otherwise, a category could be added but ignored indefinitely because
    // the cache was re-filled with the old value after the cache flush on
    // behalf of the category.

    runtimeLock.read();

    if (!cls->isRealized()) {
        // Drop the read-lock and acquire the write-lock.
        // realizeClass() checks isRealized() again to prevent
        // a race while the lock is down.
        runtimeLock.unlockRead();
        runtimeLock.write();

        realizeClass(cls);

        runtimeLock.unlockWrite();
        runtimeLock.read();
    }

    if (initialize  &&  !cls->isInitialized()) {
        runtimeLock.unlockRead();

        _class_initialize (_class_getNonMetaClass(cls, inst));
        runtimeLock.read();
        // If sel == initialize, _class_initialize will send +initialize and 
        // then the messenger will send +initialize again after this 
        // procedure finishes. Of course, if this is not being called 
        // from the messenger then it won't happen. 2778172
    }

    
 retry:    
    runtimeLock.assertReading();

    // Try this class's cache.

    imp = cache_getImp(cls, sel);
    if (imp) goto done;

    // Try this class's method lists.
    {
        Method meth = getMethodNoSuper_nolock(cls, sel);
        if (meth) {
            log_and_fill_cache(cls, meth->imp, sel, inst, cls);
            imp = meth->imp;
            goto done;
        }
    }

    // Try superclass caches and method lists.
    {
        unsigned attempts = unreasonableClassCount();
        for (Class curClass = cls->superclass;
             curClass != nil;
             curClass = curClass->superclass)
        {
            // Halt if there is a cycle in the superclass chain.
            if (--attempts == 0) {
                _objc_fatal("Memory corruption in class list.");
            }
            
            // Superclass cache.
            imp = cache_getImp(curClass, sel);
            if (imp) {
                if (imp != (IMP)_objc_msgForward_impcache) {
                    // Found the method in a superclass. Cache it in this class.
                    log_and_fill_cache(cls, imp, sel, inst, curClass);
                    goto done;
                }
                else {
                    // Found a forward:: entry in a superclass.
                    // Stop searching, but don't cache yet; call method 
                    // resolver for this class first.
                    break;
                }
            }
            
            // Superclass method list.
            Method meth = getMethodNoSuper_nolock(curClass, sel);
            if (meth) {
                log_and_fill_cache(cls, meth->imp, sel, inst, curClass);
                imp = meth->imp;
                goto done;
            }
        }
    }

    // No implementation found. Try method resolver once.

    if (resolver  &&  !triedResolver) {
        runtimeLock.unlockRead();
        _class_resolveMethod(cls, sel, inst);
        runtimeLock.read();
        // Don't cache the result; we don't hold the lock so it may have 
        // changed already. Re-do the search from scratch instead.
        triedResolver = YES;
        goto retry;
    }

    // No implementation found, and method resolver didn't help. 
    // Use forwarding.

    imp = (IMP)_objc_msgForward_impcache;
    cache_fill(cls, sel, imp, inst);

 done:
    runtimeLock.unlockRead();

    return imp;
}

這里面有個_class_initialize 

_class_initialize

void _class_initialize(Class cls)
{
    assert(!cls->isMetaClass());

    Class supercls;
    bool reallyInitialize = NO;

    // Make sure super is done initializing BEFORE beginning to initialize cls.
    // See note about deadlock above.
    supercls = cls->superclass;
    if (supercls  &&  !supercls->isInitialized()) {
        _class_initialize(supercls);
    }
    
    // Try to atomically set CLS_INITIALIZING.
    {
        monitor_locker_t lock(classInitLock);
        if (!cls->isInitialized() && !cls->isInitializing()) {
            cls->setInitializing();
            reallyInitialize = YES;
        }
    }
    
    if (reallyInitialize) {
        // We successfully set the CLS_INITIALIZING bit. Initialize the class.
        
        // Record that we're initializing this class so we can message it.
        _setThisThreadIsInitializingClass(cls);

        if (MultithreadedForkChild) {
            // LOL JK we don't really call +initialize methods after fork().
            performForkChildInitialize(cls, supercls);
            return;
        }
        
        // Send the +initialize message.
        // Note that +initialize is sent to the superclass (again) if 
        // this class doesn't implement +initialize. 2157218
        if (PrintInitializing) {
            _objc_inform("INITIALIZE: thread %p: calling +[%s initialize]",
                         pthread_self(), cls->nameForLogging());
        }

        // Exceptions: A +initialize call that throws an exception 
        // is deemed to be a complete and successful +initialize.
        //
        // Only __OBJC2__ adds these handlers. !__OBJC2__ has a
        // bootstrapping problem of this versus CF's call to
        // objc_exception_set_functions().
#if __OBJC2__
        @try
#endif
        {
            callInitialize(cls);

            if (PrintInitializing) {
                _objc_inform("INITIALIZE: thread %p: finished +[%s initialize]",
                             pthread_self(), cls->nameForLogging());
            }
        }
#if __OBJC2__
        @catch (...) {
            if (PrintInitializing) {
                _objc_inform("INITIALIZE: thread %p: +[%s initialize] "
                             "threw an exception",
                             pthread_self(), cls->nameForLogging());
            }
            @throw;
        }
        @finally
#endif
        {
            // Done initializing.
            lockAndFinishInitializing(cls, supercls);
        }
        return;
    }
    
    else if (cls->isInitializing()) {
        // We couldn't set INITIALIZING because INITIALIZING was already set.
        // If this thread set it earlier, continue normally.
        // If some other thread set it, block until initialize is done.
        // It's ok if INITIALIZING changes to INITIALIZED while we're here, 
        //   because we safely check for INITIALIZED inside the lock 
        //   before blocking.
        if (_thisThreadIsInitializingClass(cls)) {
            return;
        } else if (!MultithreadedForkChild) {
            waitForInitializeToComplete(cls);
            return;
        } else {
            // We're on the child side of fork(), facing a class that
            // was initializing by some other thread when fork() was called.
            _setThisThreadIsInitializingClass(cls);
            performForkChildInitialize(cls, supercls);
        }
    }
    
    else if (cls->isInitialized()) {
        // Set CLS_INITIALIZING failed because someone else already 
        //   initialized the class. Continue normally.
        // NOTE this check must come AFTER the ISINITIALIZING case.
        // Otherwise: Another thread is initializing this class. ISINITIALIZED 
        //   is false. Skip this clause. Then the other thread finishes 
        //   initialization and sets INITIALIZING=no and INITIALIZED=yes. 
        //   Skip the ISINITIALIZING clause. Die horribly.
        return;
    }
    
    else {
        // We shouldn't be here. 
        _objc_fatal("thread-safe class init in objc runtime is buggy!");
    }
}

這個方法的關(guān)鍵代碼是 _class_initialize(supercls); 和  if(!cls->isInitialized() && !cls->isInitializing()) {...},callInitialize(cls);
在初始化子類的時候 先遞初始化父類, 條件標識表示類沒有初始化并且不是正在初始化, callInitialize(cls)表示調(diào)用+initialize方法

callInitialize

void callInitialize(Class cls)
{
    ((void(*)(Class, SEL))objc_msgSend)(cls, SEL_initialize);
    asm("");
}
我們可以看到+initialize是通過objc_msgSend機制調(diào)用的.

總結(jié) 先調(diào)用父類的+initialize,再調(diào)用子類的+initialize
(先初始化父類,再初始化子類,每個類只會初始化1次)

+initialize和+load的很大區(qū)別是,+initialize是通過objc_msgSend進行調(diào)用的,所以有以下特點
如果子類沒有實現(xiàn)+initialize,會調(diào)用父類的+initialize(所以父類的+initialize可能會被調(diào)用多次)
如果分類實現(xiàn)了+initialize,就覆蓋類本身的+initialize調(diào)用

給分類動態(tài)綁定屬性底層原理

void objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy) {
    _object_set_associative_reference(object, (void *)key, value, policy);
}

void _object_set_associative_reference(id object, void *key, id value, uintptr_t policy) {
    // retain the new value (if any) outside the lock.
    ObjcAssociation old_association(0, nil);
    id new_value = value ? acquireValue(value, policy) : nil;
    {
        AssociationsManager manager;
        AssociationsHashMap &associations(manager.associations());
        disguised_ptr_t disguised_object = DISGUISE(object);
        if (new_value) {
            // break any existing association.
            AssociationsHashMap::iterator i = associations.find(disguised_object);
            if (i != associations.end()) {
                // secondary table exists
                ObjectAssociationMap *refs = i->second;
                ObjectAssociationMap::iterator j = refs->find(key);
                if (j != refs->end()) {
                    old_association = j->second;
                    j->second = ObjcAssociation(policy, new_value);
                } else {
                    (*refs)[key] = ObjcAssociation(policy, new_value);
                }
            } else {
                // create the new association (first time).
                ObjectAssociationMap *refs = new ObjectAssociationMap;
                associations[disguised_object] = refs;
                (*refs)[key] = ObjcAssociation(policy, new_value);
                object->setHasAssociatedObjects();
            }
        } else {
            // setting the association to nil breaks the association.
            AssociationsHashMap::iterator i = associations.find(disguised_object);
            if (i !=  associations.end()) {
                ObjectAssociationMap *refs = i->second;
                ObjectAssociationMap::iterator j = refs->find(key);
                if (j != refs->end()) {
                    old_association = j->second;
                    refs->erase(j);
                }
            }
        }
    }
    // release the old value (outside of the lock).
    if (old_association.hasValue()) ReleaseValue()(old_association);
}

class AssociationsManager {
    // associative references: object pointer -> PtrPtrHashMap.
    static AssociationsHashMap *_map;
public:
    AssociationsManager()   { AssociationsManagerLock.lock(); }
    ~AssociationsManager()  { AssociationsManagerLock.unlock(); }
    
    AssociationsHashMap &associations() {
        if (_map == NULL)
            _map = new AssociationsHashMap();
        return *_map;
    }
};

class AssociationsHashMap : public unordered_map<disguised_ptr_t, ObjectAssociationMap *, DisguisedPointerHash, DisguisedPointerEqual, AssociationsHashMapAllocator> {
    public:
        void *operator new(size_t n) { return ::malloc(n); }
        void operator delete(void *ptr) { ::free(ptr); }
    };

 class ObjectAssociationMap : public std::map<void *, ObjcAssociation, ObjectPointerLess, ObjectAssociationMapAllocator> {
    public:
        void *operator new(size_t n) { return ::malloc(n); }
        void operator delete(void *ptr) { ::free(ptr); }
    };

class ObjcAssociation {
        uintptr_t _policy;
        id _value;
    public:
        ObjcAssociation(uintptr_t policy, id value) : _policy(policy), _value(value) {}
        ObjcAssociation() : _policy(0), _value(nil) {}

        uintptr_t policy() const { return _policy; }
        id value() const { return _value; }
        
        bool hasValue() { return _value != nil; }
    };

我么從源碼中分析AssociationsManager單利中有一個AssociationsHashMap, AssociationsHashMap的key就是(disguised_ptr_t disguised_object = DISGUISE(object)是object. value是ObjectAssociationMap, ObjectAssociationMap的key是void*就是傳過來的void *key, value是ObjcAssociation

image.png

給分類動態(tài)綁定week屬性怎么搞

typedef OBJC_ENUM(uintptr_t, objc_AssociationPolicy) {
    OBJC_ASSOCIATION_ASSIGN = 0,           /**< Specifies a weak reference to the associated object. */
    OBJC_ASSOCIATION_RETAIN_NONATOMIC = 1, /**< Specifies a strong reference to the associated object. 
                                            *   The association is not made atomically. */
    OBJC_ASSOCIATION_COPY_NONATOMIC = 3,   /**< Specifies that the associated object is copied. 
                                            *   The association is not made atomically. */
    OBJC_ASSOCIATION_RETAIN = 01401,       /**< Specifies a strong reference to the associated object.
                                            *   The association is made atomically. */
    OBJC_ASSOCIATION_COPY = 01403          /**< Specifies that the associated object is copied.
                                            *   The association is made atomically. */
};

OBJC_ASSOCIATION_ASSIGN 對應(yīng)屬性的assign
OBJC_ASSOCIATION_RETAIN_NONATOMIC 對應(yīng)屬性的nonatomic strong
OBJC_ASSOCIATION_COPY_NONATOMIC 對應(yīng)屬性的nonatomic copy
OBJC_ASSOCIATION_RETAIN 對應(yīng)屬性的atomic strong
OBJC_ASSOCIATION_COPY 對應(yīng)屬性的atomic copy
我們發(fā)現(xiàn)對應(yīng)的屬性沒有week 我們怎么做呢? 我們可以用OBJC_ASSOCIATION_ASSIGN代替week但是assign和week我們知道區(qū)別assign修飾對象當對象釋放后不會自動變成nil 我們需要注意的是在對象釋放后手動吧對象變成nil.這樣是有風(fēng)險的我們可能會忘記自己當對象釋放的時候吧對象變成nil.

我們可以設(shè)計一個NSObject分類增加一個帶回調(diào)的方法.就是當對象將要釋放時候,回調(diào)block. 我們在block中把對象手動賦值nil即可

#import "UIView+weak.h"
#import <objc/runtime.h>
#import <CYLDeallocBlockExecutor/CYLDeallocBlockExecutor.h>
@implementation UIView (weak)

- (NSObject *)obj{
    return objc_getAssociatedObject(self, _cmd);
}

- (void)setObj:(NSObject *)obj{
    objc_setAssociatedObject(self, @selector(obj), obj, OBJC_ASSOCIATION_ASSIGN);
    [obj cyl_willDeallocWithSelfCallback:^(__unsafe_unretained id owner, NSUInteger identifier) {
        self.obj = nil;
    }];
    //這樣是錯的? 因為屬性雖然是 weak 但是運行時objc_setAssociatedObject這個方法沒有weak 只有OBJC_ASSOCIATION_ASSIGN  OBJC_ASSOCIATION_ASSIGN
}

@end


//
//  NSObject+CYLDeallocBlockExecutor.m
//  CYLDeallocBlockExecutor
//  v1.2.0
//  Created by 微博@iOS程序犭袁 ( http://weibo.com/luohanchenyilong/ ) on 15/12/27.
//  Copyright ? 2015年 https://github.com/ChenYilong . All rights reserved.
//

#import "NSObject+CYLDeallocBlockExecutor.h"
#import <objc/runtime.h>
#include <libkern/OSAtomic.h>
#import <pthread.h>

static const char CYLDeallocCallbackModelKey;
static dispatch_queue_t _deallocCallbackQueue = 0;
const NSUInteger CYLDeallocCallbackIllegalIdentifier = 0;

@interface CYLDeallocCallbackModel : NSObject

@property (nonatomic, assign) pthread_mutex_t lock;
@property (nonatomic, strong) NSMutableDictionary *callbacksDictionary;
@property (nonatomic, unsafe_unretained) id owner;

@end

@implementation CYLDeallocCallbackModel

- (instancetype)initWithOwner:(id)owner {
    self = [super init];
    if (self) {
        _owner = owner;
        pthread_mutex_init(&_lock, NULL);
        _callbacksDictionary = [NSMutableDictionary new];
    }
    return self;
}

- (NSUInteger)addSelfCallback:(CYLDeallocSelfCallback)selfCallback {
    static volatile int64_t globalIdentifier = 0;
    if (!selfCallback) {
        return CYLDeallocCallbackIllegalIdentifier;
    }
    // globalIdentifier 運行一次就自增1
    NSUInteger newIdentifier = (NSUInteger)OSAtomicIncrement64(&globalIdentifier);
    NSNumber *newIdentifierNumber = @(newIdentifier);
    
    if (newIdentifierNumber) {
        pthread_mutex_lock(&_lock);
        [_callbacksDictionary setObject:[selfCallback copy] forKey:newIdentifierNumber];
        pthread_mutex_unlock(&_lock);
        return newIdentifier;
    }
    return CYLDeallocCallbackIllegalIdentifier;
}

- (BOOL)removeCallbackWithIdentifier:(NSUInteger)identifier {
    if (identifier == CYLDeallocCallbackIllegalIdentifier) {
        return NO;
    }
    
    NSNumber *identifierNumber = [NSNumber numberWithUnsignedInteger:identifier];
    if (identifierNumber) {
        pthread_mutex_lock(&_lock);
        [_callbacksDictionary removeObjectForKey:identifierNumber];
        pthread_mutex_unlock(&_lock);
        return YES;
    }
    return NO;
}

- (void)removeAllCallbacks {
    pthread_mutex_lock(&_lock);
    [_callbacksDictionary removeAllObjects];
    pthread_mutex_unlock(&_lock);
}

- (void)dealloc {
    [_callbacksDictionary enumerateKeysAndObjectsUsingBlock:^(NSNumber *identifier, CYLDeallocSelfCallback block, BOOL * _Nonnull stop) {
        block(_owner, identifier.unsignedIntegerValue);
    }];
    pthread_mutex_destroy(&_lock);
}

@end

const void *CYLDeallocExecutorsKey = &CYLDeallocExecutorsKey;

@implementation NSObject (CYLDeallocBlockExecutor)

- (NSHashTable *)cyl_deallocExecutors {
    NSHashTable *table = objc_getAssociatedObject(self,CYLDeallocExecutorsKey);
    if (!table) {
        table = [NSHashTable hashTableWithOptions:NSPointerFunctionsStrongMemory];
        objc_setAssociatedObject(self, CYLDeallocExecutorsKey, table, OBJC_ASSOCIATION_RETAIN);
    }
    return table;
}

- (void)cyl_executeAtDealloc:(CYLDeallocExecutorBlock)block {
    if (block) {
        CYLDeallocExecutor *executor = [[CYLDeallocExecutor alloc] initWithBlock:block];
        dispatch_sync(self.deallocCallbackQueue, ^{
            [[self cyl_deallocExecutors] addObject:executor];
        });
    }
}

- (dispatch_queue_t)deallocCallbackQueue {
    if (_deallocCallbackQueue == nil) {
        static dispatch_once_t onceToken;
        dispatch_once(&onceToken, ^{
            _deallocCallbackQueue = dispatch_queue_create("com.chenyilong.CYLDeallocBlockExecutor.deallocCallbackQueue", DISPATCH_QUEUE_SERIAL);
        });
    }
    return _deallocCallbackQueue;
}

- (NSUInteger)cyl_willDeallocWithSelfCallback:(CYLDeallocSelfCallback)selfCallback {
    if (!selfCallback) {
        return CYLDeallocCallbackIllegalIdentifier;
    }
    __block CYLDeallocCallbackModel *model = nil;
    dispatch_sync(self.deallocCallbackQueue, ^{
        model = objc_getAssociatedObject(self, &CYLDeallocCallbackModelKey);
        if (!model) {
            model = [[CYLDeallocCallbackModel alloc] initWithOwner:self];
            objc_setAssociatedObject(self, &CYLDeallocCallbackModelKey, model, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
        }
    });
    NSUInteger newIdentifier = [model addSelfCallback:selfCallback];
    return newIdentifier;
}

- (BOOL)cyl_cancelDeallocCallbackWithIdentifier:(NSUInteger)identifier {
    CYLDeallocCallbackModel *model = objc_getAssociatedObject(self, &CYLDeallocCallbackModelKey);
    if (model) {
        return [model removeCallbackWithIdentifier:identifier];
    }
    return NO;
}

- (void)cyl_cancelAllDeallocCallbacks {
    CYLDeallocCallbackModel *model = objc_getAssociatedObject(self, &CYLDeallocCallbackModelKey);
    if (model) {
        [model removeAllCallbacks];
    }
}
@end

上面的代碼來自 https://github.com/ChenYilong

__weak的底層實現(xiàn)

__weak的使用場景這里就不多說了這主要 分析一下__weak修飾的修飾的對象在釋放后自動變成nil的這個過程
看下面代碼吧

- (void)dealloc {
    _objc_rootDealloc(self);
}

_objc_rootDealloc(id obj)
{
    assert(obj);

    obj->rootDealloc();
}

inline void
objc_object::rootDealloc()
{
    if (isTaggedPointer()) return;  // fixme necessary?

    if (fastpath(isa.nonpointer  &&    //標識nonpointer 是否是優(yōu)化過的類
                 !isa.weakly_referenced  &&   //是否有被弱引用指向過
                 !isa.has_assoc  &&  // 是否有設(shè)置過關(guān)聯(lián)對象
                 !isa.has_cxx_dtor  &&   // 是否有C++的析構(gòu)函數(shù)
                 !isa.has_sidetable_rc)) // 是否引用計數(shù)器是否過大無法存儲在isa中
    {
        assert(!sidetable_present());
        free(this);
    } 
    else {
        //如果有弱引用對象指向過 走這里
        object_dispose((id)this);
    }
}

id  object_dispose(id obj)
{
    if (!obj) return nil;

    objc_destructInstance(obj);    
    free(obj);

    return nil;
}

void *objc_destructInstance(id obj) 
{
    if (obj) {
        // Read all of the flags at once for performance.
        bool cxx = obj->hasCxxDtor();
        bool assoc = obj->hasAssociatedObjects();

        // This order is important.
        if (cxx) object_cxxDestruct(obj); //如果有c++的析構(gòu)函數(shù)就去執(zhí)行析構(gòu)函數(shù)
        if (assoc) _object_remove_assocations(obj); //如果綁定過對象 就去解除綁定
        obj->clearDeallocating();
    }

    return obj;
}

inline void objc_object::clearDeallocating()
{
    if (slowpath(!isa.nonpointer)) {
        // Slow path for raw pointer isa. 不是優(yōu)化過的類
        sidetable_clearDeallocating();
    }
    else if (slowpath(isa.weakly_referenced  ||  isa.has_sidetable_rc)) {
        // Slow path for non-pointer isa with weak refs and/or side table data. 我們是到這個分支的
        clearDeallocating_slow();
    }

    assert(!sidetable_present());
}

NEVER_INLINE void
objc_object::clearDeallocating_slow()
{
    assert(isa.nonpointer  &&  (isa.weakly_referenced || isa.has_sidetable_rc));

    SideTable& table = SideTables()[this];
    table.lock();
    if (isa.weakly_referenced) {
        //我們看  關(guān)鍵代碼
        weak_clear_no_lock(&table.weak_table, (id)this);
    }
    if (isa.has_sidetable_rc) {
        table.refcnts.erase(this);
    }
    table.unlock();
}

void 
weak_clear_no_lock(weak_table_t *weak_table, id referent_id) 
{
    objc_object *referent = (objc_object *)referent_id;

    weak_entry_t *entry = weak_entry_for_referent(weak_table, referent);
    if (entry == nil) {
        /// XXX shouldn't happen, but does with mismatched CF/objc
        //printf("XXX no entry for clear deallocating %p\n", referent);
        return;
    }

    // zero out references
    weak_referrer_t *referrers;
    size_t count;
    
    if (entry->out_of_line()) {
        referrers = entry->referrers;
        count = TABLE_SIZE(entry);
    } 
    else {
        referrers = entry->inline_referrers;
        count = WEAK_INLINE_COUNT;
    }
    
    for (size_t i = 0; i < count; ++i) {
        objc_object **referrer = referrers[i];
        if (referrer) {
            if (*referrer == referent) {
                *referrer = nil;
            }
            else if (*referrer) {
                _objc_inform("__weak variable at %p holds %p instead of %p. "
                             "This is probably incorrect use of "
                             "objc_storeWeak() and objc_loadWeak(). "
                             "Break on objc_weak_error to debug.\n", 
                             referrer, (void*)*referrer, (void*)referent);
                objc_weak_error();
            }
        }
    }
    
    weak_entry_remove(weak_table, entry);
}


我們可以這樣理解
SideTable& table = SideTables()[this]; //一個全局字典
以對象(self)為key 里面 value 也是一個字典 table(weak_table)

weak_entry_t *entry = weak_entry_for_referent(weak_table, referent);

weak_table(字典)根據(jù)key(referent) 取出entry entry-> referrers相當于數(shù)組里面存的是 指向?qū)ο?self)的__weak指針
便利這個數(shù)組把所有__weal指針手動賦值為nil.

以上就是 __weak指針當對象釋放的時候 自動會清空的大致過程.

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

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

  • 一、Runtime簡介 Runtime簡稱運行時。OC就是運行時機制,也就是在運行時候的一些機制,其中最主要的是消...
    林安530閱讀 1,112評論 0 2
  • 文中的實驗代碼我放在了這個項目中。 以下內(nèi)容是我通過整理[這篇博客] (http://yulingtianxia....
    茗涙閱讀 1,021評論 0 6
  • 我們常常會聽說 Objective-C 是一門動態(tài)語言,那么這個「動態(tài)」表現(xiàn)在哪呢?我想最主要的表現(xiàn)就是 Obje...
    Ethan_Struggle閱讀 2,319評論 0 7
  • 繼上Runtime梳理(四) 通過前面的學(xué)習(xí),我們了解到Objective-C的動態(tài)特性:Objective-C不...
    小名一峰閱讀 838評論 0 3
  • Runtime簡介 Runtime是什么呢?比較官方的解釋是 Runtime 又叫運行時,是一套底層的 C 語言 ...
    大基本功閱讀 167評論 0 1

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