对象的本质 Class结构 + load + initialize 对象动态绑定 __weak底层实现

对象的本质

NSObject * obj = [[NSObject alloc] init];
转化成c++可以到本质是一个结构体

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

可以看出isa 是指向objc_class结构的指针
obj对象的地址,就是结构体的地址,结构体里面第一个元素就是isa,结构体的地址也就是isa指针的地址.
总结对象的本质就是一个指向类结构体的指针 我们NSLog(@"%@", obj)其实是打印了一个 指向NSObject这个类的指针的地址.
下面是一个Student转成的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结构体中isa实例变量指向的地址
(Class) $14 = 0x0000000105c37a48 LCKVCtest
(lldb) p/x 0x0000000105c37a48 & 0x0000000ffffffff8ULL  // 对象中的isa& ISA_MASK得到类的地址
(unsigned long long) $15 = 0x0000000105c37a48
(lldb) p/x &(kvc->isa) 得到结构体isa的地址(isa是结构体中第一个成员变量所以也是结构体的地址也就是对象的地址)
(__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对象转正我们自定的结构体指针类对象中isa& ISA_MASK指向元类对象 元类对象的中的isa& ISA_MASK指向NSObejc元类,NSObejc元类中的isa指向自己. (不用& ISA_MASK)

描述对象类和元类的关系这里来个张神图镇场再合适不过了


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;
这里是类对象的内存布局 但是遗憾的是!__OBJC2__ 已经不是现在的版本了.
现在的版本是__OBJC2__ 
并且OBJC2_UNAVAILABLE 说明在__OBJC2__版本下这个结构体也是不可用的,我们可以简单分析一下

isa 可以简单的人为是指向元类(其实需要$ISA_MASK)
super_class 指向父类
ivars 是成员变量列表
objc_method_list 方法列表
cache 方法缓存
protocols 协议类表

现在的class 结构如下

//对象结构体
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 结构体
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));
};


以上是现在objc_class现在的内存布局 我们摘出存在objc_class结构体内存里面的关键信息

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架构之前,isa就是一个普通的指针,存储着Class、Meta-Class对象的内存地址
从arm64架构开始,对isa进行了优化,变成了一个共用体(union)结构,还使用位域来存储更多的信息

image.png

nonpointer
0,代表普通的指针,存储着Class、Meta-Class对象的内存地址
1,代表优化过,使用位域存储更多的信息

has_assoc
是否有设置过关联对象,如果没有,释放时会更快

has_cxx_dtor
是否有C++的析构函数(.cxx_destruct),如果没有,释放时会更快

shiftcls
存储着Class、Meta-Class对象的内存地址信息

magic
用于在调试时分辨对象是否未完成初始化

weakly_referenced
是否有被弱引用指向过,如果没有,释放时会更快

deallocating
对象是否正在释放

extra_rc
里面存储的值是引用计数器减1

has_sidetable_rc
引用计数器是否过大无法存储在isa中
如果为1,那么引用计数会存储在一个叫SideTable的类的属性中

分类底层的实现

分类在runtime中结构是这样的我们每一个分类 都会生成一个这样的结构体
下面是分类在objc代码源码中的结构体

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 看看分类转换成的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
};

//关于协议在分类中
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;
};

我们从上面可以看到一个类的一个分类底层其实是一个结构体.
结构体中包含的有
1类的 名字
2..
3实例方法列表
4对象方法类表
5协议类表
6属性列表

我们知道一个对象调用一个实例方法寻找这个方法其实是在类里面的寻找的,如果我们调用一个分类的实例方法,是怎么找到这个方法的呢?

我们根据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 是分类的个数
    bool fromBundle = NO;
    while (i--) {
        auto& entry = cats->list[I];

        method_list_t *mlist = entry.cat->methodsForMeta(isMeta);//我看可以看到因为i--所以所以取得是最后一个分类的方法类表 根据isMeta判断取得是 实例方法还是对象方法
        if (mlist) {
            mlists[mcount++] = mlist;// 我们取到的方法类表 放到mlists这个二维数组中
            fromBundle |= entry.hi->isBundle();
        }

        //下面属性 和协议和上面类似

        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返回的就是这个结构体指针 上面分析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);
}

//这是关键代码上下文略去了 
void attachLists(List* const * addedLists, uint32_t addedCount) {
        if (addedCount == 0) return;

        if (hasArray()) { //hasArray()这个方法表示自己是否有元素也就是 rw->methods是否为空 (如果一个类本身没有任何实例方法,和有自己的实例方法 在合并分类里面的实例方法根据这个条件分情况考虑)
            // many lists -> many lists
            uint32_t oldCount = array()->count; //数组原来的长度
            uint32_t newCount = oldCount + addedCount; //加上所有分类后的长度
            setArray((array_t *)realloc(array(), array_t::byteSize(newCount))); 分配新的空间
            array()->count = newCount;
            
            // 从array()->lists指向的那块内存开始拷贝 拷贝长度为oldCount 拷贝到array()->lists + addedCount起始的地址 (array()->lists的内容移动了 但是array()->lists指向还没变)
            memmove(array()->lists + addedCount,
                    array()->lists,
                    oldCount * sizeof(array()->lists[0]));
            // 从addedLists指向的那块内存开始拷贝 拷贝长度为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]));
        }
    }


这个方法是吧一个类的所有分类里面的,方法,协议,属性,存到类里面

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));
首先根据分类的个数通过malloc分配三个 二维数组. 我们通过分析method_list_t * * mlists存到东西来分析一下为什么是二维数组
[
[数组1里面存的是ClassA_ClassA_category_test1的所有对象方法],
[数组2里面存的是ClassA_ClassA_category_test2的所有对象方法],
[数组3里面存的是ClassA_ClassA_category_test2的所有对象方法]
]
一个类可以有 很多很多分类 一个分类可以有 很多对象方法

我们重点分析一下 while看上面代码注释
经过上面的分析我们 可以得出下面的结论
分类其实就是一个结构体 包含了,方法列表,属性列表,协议列表
进过runtime会吧分类里面的信息插入到类的前面

介绍你知道的关于所有load方法所有的知识点

(1)load方法的调用时机
(2)load调用方法的调用顺序
load方法的调用时机是在runtime初始化的时候调用的,在main函数前面,我们分析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便利所有分类生成一个结构体数组
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 会递归的找一个类的父类, 
调用add_class_to_loadable_list,如果在这个方法中会判断类中是否实现了+load方法如果实现了就会吧 这个类和+load方法作为成员变量存在一个结构体数组中. 这样就生成了一个存着 类,和这个类对应的load方法的结构体数组 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一个结构体数组
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++;
}

//下面是结构体的结构
struct loadable_class {
    Class cls;  // may be nil
    IMP method;
};

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

总结:经过上面的源码分析我们发现 runtime初始化期间会 生成一个loadable_class数组结构体并且是按照加载类的顺序,并且父类在前面子类在后的顺序 和loadable_category数组结构体顺序和加载分类的顺序相同

下面我们在分析剩下的部分直到找到+load是怎么自动调动的揭开+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数组的长度

从方法名字可以看出
call_class_loads //调用类的load方法
call_category_loads //调用分类的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数组的长度
    loadable_classes = nil;
    loadable_classes_allocated = 0;
    loadable_classes_used = 0; //赋值成0为了结束while (loadable_classes_used > 0) {...}循环
        

    
    // Call all +loads for the detached list.
    for (i = 0; i < used; i++) {
       //我们还记得loadable_class结构体的结构吧 里面有+load方法的实现 有类
        Class cls = classes[i].cls; //取出结构体里面的类
        load_method_t load_method = (load_method_t)classes[i].method; //取出结构体里面的load方法指针
        if (!cls) continue; 

        if (PrintLoading) {
            _objc_inform("LOAD: +[%s load]\n", cls->nameForLogging());
        }
        (*load_method)(cls, SEL_load); //用load函数指针直接调用.没有走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;
        }
    }
  ... 
}
//我们看出调用分类的load方法 和调用类里面的load方法差不都 也是便利拿出 loadable_categorie 结构体再取出 分类通过分类取出类, 再拿出load方法直接通过 结构体中存的load函数指针调用load方法.

到这里load的源码部分基本分析完了,其实从头开始看也不难,我现在总结一下
总结
1如果有类实现了+load方法 先加载类的+load方法不管编译顺序谁前谁后
2加载类的+load方法和类的加载顺序有关系 谁在前面先调用谁但是在调用类的+load方法之前会递归的判断父类是否实现了+load方法父类实现了就先调用父类的+load方法
3分类的+load和分类的加载顺序一样
4调用+load的过程是在runtime初始化的时候会先便利所有类便利顺序和类的加载顺序一样 判断类是否实现了+load方法如果yes就用递归去便利父类.这样吧所有实现load方法的 类和load函数指针存在结构体中 分类同理 在调用的+load方法时再便利结构体数组通过+load函数指针直接调用 分类同理

initialize

1调用时机
2调用过程
+initialize方法会在类第一次接收到消息时调用 所以分析一下类接受消息的的一个过程 关键代码在 查找类里面消息这一步

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!");
    }
}

这个方法的关键代码是 _class_initialize(supercls); 和  if(!cls->isInitialized() && !cls->isInitializing()) {...},callInitialize(cls);
在初始化子类的时候 先递初始化父类, 条件标识表示类没有初始化并且不是正在初始化, callInitialize(cls)表示调用+initialize方法

callInitialize

void callInitialize(Class cls)
{
    ((void(*)(Class, SEL))objc_msgSend)(cls, SEL_initialize);
    asm("");
}
我们可以看到+initialize是通过objc_msgSend机制调用的.

总结 先调用父类的+initialize,再调用子类的+initialize
(先初始化父类,再初始化子类,每个类只会初始化1次)

+initialize和+load的很大区别是,+initialize是通过objc_msgSend进行调用的,所以有以下特点
如果子类没有实现+initialize,会调用父类的+initialize(所以父类的+initialize可能会被调用多次)
如果分类实现了+initialize,就覆盖类本身的+initialize调用

给分类动态绑定属性底层原理

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

给分类动态绑定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 对应属性的assign
OBJC_ASSOCIATION_RETAIN_NONATOMIC 对应属性的nonatomic strong
OBJC_ASSOCIATION_COPY_NONATOMIC 对应属性的nonatomic copy
OBJC_ASSOCIATION_RETAIN 对应属性的atomic strong
OBJC_ASSOCIATION_COPY 对应属性的atomic copy
我们发现对应的属性没有week 我们怎么做呢? 我们可以用OBJC_ASSOCIATION_ASSIGN代替week但是assign和week我们知道区别assign修饰对象当对象释放后不会自动变成nil 我们需要注意的是在对象释放后手动吧对象变成nil.这样是有风险的我们可能会忘记自己当对象释放的时候吧对象变成nil.

我们可以设计一个NSObject分类增加一个带回调的方法.就是当对象将要释放时候,回调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的底层实现

__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 是否是优化过的类
                 !isa.weakly_referenced  &&   //是否有被弱引用指向过
                 !isa.has_assoc  &&  // 是否有设置过关联对象
                 !isa.has_cxx_dtor  &&   // 是否有C++的析构函数
                 !isa.has_sidetable_rc)) // 是否引用计数器是否过大无法存储在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++的析构函数就去执行析构函数
        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. 不是优化过的类
        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) {
        //我们看  关键代码
        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(字典)根据key(referent) 取出entry entry-> referrers相当于数组里面存的是 指向对象(self)的__weak指针
便利这个数组把所有__weal指针手动赋值为nil.

以上就是 __weak指针当对象释放的时候 自动会清空的大致过程.

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,122评论 6 505
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,070评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,491评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,636评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,676评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,541评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,292评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,211评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,655评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,846评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,965评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,684评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,295评论 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,894评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,012评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,126评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,914评论 2 355

推荐阅读更多精彩内容

  • 一、Runtime简介 Runtime简称运行时。OC就是运行时机制,也就是在运行时候的一些机制,其中最主要的是消...
    林安530阅读 1,064评论 0 2
  • 文中的实验代码我放在了这个项目中。 以下内容是我通过整理[这篇博客] (http://yulingtianxia....
    茗涙阅读 923评论 0 6
  • 我们常常会听说 Objective-C 是一门动态语言,那么这个「动态」表现在哪呢?我想最主要的表现就是 Obje...
    Ethan_Struggle阅读 2,195评论 0 7
  • 继上Runtime梳理(四) 通过前面的学习,我们了解到Objective-C的动态特性:Objective-C不...
    小名一峰阅读 754评论 0 3
  • Runtime简介 Runtime是什么呢?比较官方的解释是 Runtime 又叫运行时,是一套底层的 C 语言 ...
    大基本功阅读 109评论 0 1