Isa and class chart

As can be seen from the picture above

The isa of an object points to the class, the ISA of the class points to the metaclass, the ISA of the metaclass points to the root metaclass, and the ISA of the root metaclass points to the root metaclass.

2. The class inherits from its parent, which inherits from the root parent, which inherits from nil. The metaclass inherits the parent metaclass, which inherits the root metaclass, which inherits the root metaclass.

The structure of the class

The new objC source code and the old OBJC is not the same, here is mainly the analysis of the new objC source code. The new source version is objC4-818.2

Struct objc_object: struct objc_object; struct objc_object: struct objc_object; Class superclass; cache_t cache; // formerly cache pointer and vtable class_data_bits_t bits; // class_rw_t * plus custom rr/alloc flags class_rw_t *data() const { 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); } // The rest of the source code is not posted}Copy the code

This is the main structure of a class, where superclass is the parent, cache is the cache, and bits contain methods, attributes, proxies, and member variables.

1. cacheThe structure of the

See the structure of 'cache_t' in the source code. You can see that 'cache_t' is a commonwealth domain.Copy the code

struct cache_t {
private:
    explicit_atomic<uintptr_t> _bucketsAndMaybeMask;
    union {
        struct {
            explicit_atomic<mask_t>    _maybeMask;
#if __LP64__
            uint16_t                   _flags;
#endif
            uint16_t                   _occupied;
        };
        explicit_atomic<preopt_cache_t *> _originalPreoptCache;
    };
   
   void incrementOccupied();
public:
    // The following four fields are public for objcdt's use only.
    // objcdt reaches into fields while the process is suspended
    // hence doesn't care for locks and pesky little details like this
    // and can safely use these.
    
    unsigned capacity() const;
    struct bucket_t *buckets() const;
    Class cls() const;
    
    void insert(SEL sel, IMP imp, id receiver);

};

Copy the code
JCPerson *p  = [JCPerson alloc]; //class_data_bits_t  cache_t
Class pClass = [JCPerson class];
//        p.name       = @"djc";
//        p.age        = 18;
    
[p sayHello];
[p sayCode];
[p sayMaster];
Copy the code

JCPerson is viewed through breakpoint debug LLDB

Cache_t (LLDB) p (cache_t) pClass (Class) $0 = 0x00000001000082B0 JCPerson *) 0x00000001000082C0 (cache_t *) $1 = 0x00000001000082C0 _bucketsAndMaybeMask = { std::__1::atomic<unsigned long> = { Value = 4298515408 } } = { = { _maybeMask = { std::__1::atomic<unsigned int> = { Value = 0 } } _flags = 32804 _occupied = 0 } _originalPreoptCache = { STD ::__1::atomic<preopt_cache_t *> = {Value = 0x0000802400000000}}}} $buckets(LLDB) p $2.buckets() (bucket_t *) $3 = 0x00000001003623d0 (lldb) p *$3 (bucket_t) $4 = { _sel = { std::__1::atomic<objc_selector *> = (null) { Value = Nil}} _imp = {STD ::__1::atomic<unsigned long> = {Value = 0}}} sel(LLDB) p $4.sel() (sel) $5 = (null)Copy the code
  • When not executedsayHelloMethods beforep $4.sel()Get isnull;_occupied0; Note There is no cache method.
(lldb) p *$1
(cache_t) $8 = {
  _bucketsAndMaybeMask = {
    std::__1::atomic<unsigned long> = {
      Value = 4311840848
    }
  }
   = {
     = {
      _maybeMask = {
        std::__1::atomic<unsigned int> = {
          Value = 3
        }
      }
      _flags = 32804
      _occupied = 1
    }
    _originalPreoptCache = {
      std::__1::atomic<preopt_cache_t *> = {
        Value = 0x0001802400000003
      }
    }
  }
}

(lldb) p $8.buckets()
(bucket_t *) $10 = 0x000000010101785

(lldb) p $10->sel()
(SEL) $12 = "sayHello"
Copy the code
  • When you performsayHelloafter_occupied1.p $10->sel()return(SEL) $12 = "sayHello".
2. cache_tPrinciples of caching methods

You can see from LLDB debugging above that cache_t caches methods after they are executed. How is this implemented?

void cache_t::insert(SEL sel, IMP imp, Id receiver) {// Part of the source // get the number // Use the cache as-is if until we expected fill ratio. Mask_t newOccupied = occupied() + 1; unsigned oldCapacity = capacity(), capacity = oldCapacity; Slowpath (isConstantEmptyCache())) {// Cache is read-only. Replace it. If (! capacity) capacity = INIT_CACHE_SIZE; // allocate the size of INIT_CACHE_SIZE=4 reallocate(oldCapacity, capacity, /* freeOld */false); } else if (fastPath (newOccupied + CACHE_END_MARKER <= cache_fill_ratio(capacity)) {// The cache exists and the cache size is less than 3/4 or 7/8 Cache is less than 3/4 or 7/8 full. Use it as-is. } #if CACHE_ALLOW_FULL_UTILIZATION else if (capacity <= FULL_UTILIZATION_CACHE_SIZE && newOccupied + CACHE_END_MARKER <= capacity) { // Allow 100% cache utilization for small Buckets. Use it as-is.} #endif else {// If the cache exists and is larger than the allocated size. MAX_CACHE_SIZE = 1 << 16 Size capacity = capacity? capacity * 2 : INIT_CACHE_SIZE; if (capacity > MAX_CACHE_SIZE) { capacity = MAX_CACHE_SIZE; } reallocate(oldCapacity, capacity, true); } bucket *b = buckets(); // get subscript mask_t m = capacity - 1; Mask_t begin = cache_hash(sel, m); mask_t i = begin; // there is guaranteed to be an empty slot. // there is guaranteed to be an empty slot Store each method do {if (fastPath (b[I].sel() == 0)) {incrementOccupied(); b[i].set<Atomic, Encoded>(b, sel, imp, cls()); return; } if (b[i].sel() == sel) { // The entry was added to the cache by some other thread // before we grabbed the cacheUpdateLock. return; } } while (fastpath((i = cache_next(i, m)) ! = begin)); bad_cache(receiver, (SEL)sel); #endif // ! DEBUG_TASK_THREADS }Copy the code

3.bitsstructure

Get the first address of the class, then get the contents of 'bits' in' class_data_bits_t 'in cheaper quantities. `Person *p = [Person alloc]; 'gets the first address from' x/4gx person. class ', then offsets 32 bits' P (class_datA_bits_t *)0x100008220 'to get' bits' information, and gets' class_rw_T 'information.Copy the code
(lldb) x/4gx Person.class
0x100008200: 0x00000001000081d8 0x000000010036a140
0x100008210: 0x00000001003623d0 0x0000802400000000

(lldb) p (class_data_bits_t *)0x100008220
(class_data_bits_t *) $3 = 0x0000000100008200
(lldb) p $3->data()
(class_rw_t *) $4 = 0x00000001000081d8
(lldb) p *$4
(class_rw_t) $5 = {
  flags = 3580144
  witness = 1
  ro_or_rw_ext = {
    std::__1::atomic<unsigned long> = {
      Value = 4298547440
    }
  }
  firstSubclass = 0x000000010070fd40
  nextSiblingClass = 0x0002e03500000003
}

Copy the code
Struct class_rw_t {// Be warned that Symbolication knows the layout of this structure. Public: // List of member variables const class_ro_t *ro() const {auto v = get_ro_or_rwe(); if (slowpath(v.is<class_rw_ext_t *>())) { return v.get<class_rw_ext_t *>(&ro_or_rw_ext)->ro; } return v.get<const class_ro_t *>(&ro_or_rw_ext); } void set_ro(const class_ro_t *ro) { auto v = get_ro_or_rwe(); if (v.is<class_rw_ext_t *>()) { v.get<class_rw_ext_t *>(&ro_or_rw_ext)->ro = ro; } else { set_ro_or_rwe(ro); } // List of methods const method_array_t methods() const {auto v = get_ro_or_rwe(); if (v.is<class_rw_ext_t *>()) { return v.get<class_rw_ext_t *>(&ro_or_rw_ext)->methods; } else { return method_array_t{v.get<const class_ro_t *>(&ro_or_rw_ext)->baseMethods()}; }} // Property method list const property_array_t properties() const {auto v = get_ro_or_rwe(); if (v.is<class_rw_ext_t *>()) { return v.get<class_rw_ext_t *>(&ro_or_rw_ext)->properties; } else { return property_array_t{v.get<const class_ro_t *>(&ro_or_rw_ext)->baseProperties}; Const protocol_array_t protocols() const {auto v = get_ro_or_rwe(); if (v.is<class_rw_ext_t *>()) { return v.get<class_rw_ext_t *>(&ro_or_rw_ext)->protocols; } else { return protocol_array_t{v.get<const class_ro_t *>(&ro_or_rw_ext)->baseProtocols}; }}};Copy the code

View property methods in the class proxy method the following view property methods for example

P $4->properties() (const property_array_t) $5 = {list_array_tt<property_t, property_list_t, RawPtr> = { = { list = { ptr = 0x0000000100008220 } arrayAndFlag = 4295000608 } } } (lldb) p $5.list (const RawPtr<property_list_t>) $6 = { ptr = 0x0000000100008220 } (lldb) p $6.ptr (property_list_t *const) $7 = 0x0000000100008220 (lldb) p *$7 (property_list_t) $7 = { entsize_list_tt<property_t, property_list_t, 0, PointerModifierNop> = (entsizeAndFlags = 16, count = 2) } (lldb) p $7.get(0) (property_t) $8 = (name = "name", attributes = "T@\"NSString\",C,N,V_name")Copy the code