• In previous articles, we explored the nature of objects:
    • Alloc flow analysis of OC object principle
    • OC objects have internal alignment
    • The nature of objects in the OC object principle​
  • After understanding the nature of objects, do you have questions like:
    • What is the nature of a class?
    • What is the structure of a class?
    • Where are the properties and methods we use stored?
  • With these questions in mind, let’s explore the nature of the next class.

The structure of the class

  • Let’s start with the underlying implementation of the following code:
@interface LGPerson : NSObject
@property (nonatomic) NSString *name;
@end

@implementation LGPerson
@end

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        LGPerson  *p = [LGPerson alloc];
        NSLog(@"p:%@", p);
    }
    return 0;
}
Copy the code
  • useclangCompile:clang -rewrite-objc main.m -o main.cpp
// ...
struct objc_class;
typedef struct objc_class *Class;
// ...
struct NSObject_IMPL {
	Class isa;
};
// ...
typedef struct objc_object LGPerson;
struct LGPerson_IMPL {
	struct NSObject_IMPL NSObject_IVARS;
	NSString *_name;
};
// ...
Copy the code
  • As you can see from the compiled results :(code irrelevant to this analysis has been omitted)
    • LGPersonClasses are actuallystruct objc_objecttype
    • LGPersonThe concrete implementation ofLGPerson_IMPLThe first member isNSObject_IMPLIn theisa
    • isaIs of typeClass.ClassIs of typestruct objc_class
    • objc_classIt’s just a declaration, not a definition, so we can look at itobjcIs there a definition in the source code
  • objcThe source codeobjc_classDefinition:
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
    // ...
}

struct objc_object {
    isa_t isa;
}
Copy the code
  • fromobjcYou can see the source code,objc_classInherited fromobjc_object, the first member variable isisa
  • In the previous article we examined the object’sisaPoint to a class, so what does a class’s ISA point to?

Class of isa

  • Take a look at the isa reference of the class using the following code
@interface LGPerson : NSObject
@end
@implementation LGPerson
@end
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        LGPerson  *p = [LGPerson alloc];
    }
    return 0;
}
Copy the code
  • LLDBprintisaInformation:
    • x/4gx: Prints data stored in memory
    • p/x: Output in hexadecimal format
    • 0x00007ffffffffff8Is:ISA_MASKThe value of the&The purpose of this value is to fetchNONPOINTER_ISAIn theisavalue

  • As you can see from the print above
    • Instance variable ofisaPointing to the classLGPerson
    • LGPersonOf the classisaPoint to anotherLGPersonClass (these twoLGPersonClasses are not the same because they have different addresses, actually the secondLGPersonClasses are metaclasses.
    • printLGPersonThe memory address of the class, known by this validation, of the objectisaPointing to class, of classisaPointing to the metaclass

Type of relationship

  • The basic concept
    • The root class: inOCAlmost all classes in theNSObjectClass,NSObjectThe class is the root class, and the parent of the root class isnil
    • Metaclass: in our peacetime development will use class method and instance method, but there is no such distinction in the implementation of the underlying, in fact, are through the instance method to find, in order to distinguish these two methods, the concept of the introduction of metaclass, and then the instance method stored in the class, class method stored in the metaclass. Of the classisaPoint to metaclass.
    • Root metaclass: is the root classNSObjecttheisaPoint to the class
  • The diagram

  • Conclusion:
    • isaTo:
      • Instance variable ofisaPoint to the corresponding class
      • Of the classisaPoint to the corresponding metaclass
      • metaclassisaPoint to the root metaclass
      • A metaclassisaPoint to their own
    • Class inheritance:
      • Of the classsuperclassPoints to the parent class
      • Of the parent classsuperclassPoint to the root class
      • The root classsuperclassPoint to thenil
    • Metaclass inheritance:
      • metaclasssuperclassA metaclass that points to the parent of the corresponding class
      • Of the metaclass of the parent classsuperclassPoint to the root metaclass
      • A metaclasssuperclassPoint to the root class
      • The root classsuperclassPoint to thenil

Addendum: translation value is stored in the structure

  • For example:
struct LGStruct {
    int  a; // Offset: 0, accounting for 4 bytes
    char b; // Offset: 4, accounting for 1 byte
    int  c; // Offset: 8, accounting for 4 bytes
    long d; // Offset: 16, 8 bytes (the starting position of the member variable is an integer multiple of the memory occupied by the current member variable)
};

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        struct LGStruct s;
        s.a = 10;
        s.b = 'a';
        s.c = 30;
        s.d = 40;
    }
    return 0;
}
Copy the code
  • Retrieves the value of a member variable:
    • First determine the structure variable address
    • Determines the offset position of the member variable
    • Calculates the memory address of a member variable: structure variable address + the offset position of the variable
    • Converts the computed address to a pointer type of the variable’s corresponding type (e.gaisintType, converts the computed address toint *Type, the purpose of this turn is to extractintType value)

Class of storage

  • From the previous analysis, we have a general understanding of the relationships between classes and the structure of the class, but we are not clear about how the class is stored in memory.
    • Where are properties stored?
    • Where are member variables stored?
    • Where are methods stored?
  • So let’s go throughLLDBCombined with the source code of the way to analyze the next class in memory storage

The bits analysis of the class

  • The following debugging analysis is neededobjcSource code project, this article debugging source code address:Gitee.com/jrcode/ios_…
  • LGPersonDefinition:
@interface LGPerson : NSObject {
    NSString *subject;
}

@property (nonatomic.copy) NSString *name;
- (void)sayHello;
+ (void)say666;

@end

@implementation LGPerson
- (void)sayHello {}
+ (void)say666 {}
@end
Copy the code
  • As you can see from the above class structure, there are four main member variables in the class:
    • Class ISA;: isa pointer
    • Class superclass;: the parent class
    • cache_t cache;: Cache related
    • class_data_bits_t bits;
  • As you can see from the class member variables, the data in the class should be stored inbitsIn, the following key analysisbits
  • First of all to seecache_tDefinition:
struct cache_t {
    explicit_atomic<uintptr_t> _bucketsAndMaybeMask;
    union {
        struct {
            explicit_atomic<mask_t>    _maybeMask;	// uint32_t
#if __LP64__	
            uint16_t                   _flags;		/ / 2 bytes
#endif
            uint16_t                   _occupied; 	/ / 2 bytes
        };
        explicit_atomic<preopt_cache_t *> _originalPreoptCache;
    };
    // Static variables and methods are memory free
}
Copy the code
  • fromcache_tThe definition of can be seen as:
    • The first member variable_bucketsAndMaybeMaskforuintptr_tType, which is actuallyunsigned longThe type,8byte
    • The second member variable is union (union members share the same block of memory)
      • The first member of the consortium is the structure, which occupies the largest proportion8byte
      • The second member of the union is of type pointer, accounting for8byte
      • Therefore, the memory size of the union is the memory size of the maximum member, i.e8byte
    • So,cache_tMemory is16byte
  • bitsData acquisition:
    • It can be obtained by the method of storing translation value in the structure introduced earlierbitsThe value of the
    • In obtainingbitsYou need to determine the value ofbitsThe offset
    • The first two member variablesISAandsuperclassAre allClassType, andClassforstructobjc_class *Type, 8 bytes
    • The third member variablecacheforcache_tThe type,16byte
    • So, member variablesbitsThe offset address of is32Bytes (address + can be stored in the structure body0x20Calculated)
  • class_data_bits_tDefinition:
struct class_data_bits_t {
    friend objc_class;

    // Values are the FAST_ flags above.
    uintptr_t bits;
	// ...
    class_rw_t* data() const {
        return (class_rw_t *)(bits & FAST_DATA_MASK);
    }
    // ...
};
Copy the code
  • fromclass_data_bits_tThe definition of can be seen throughdata()Get the data and the result isclass_rw_ttype
  • class_rw_tDefinition:
struct class_rw_t {
    // Be warned that Symbolication knows the layout of this structure.
    uint32_t flags;
    uint16_t witness;
#if SUPPORT_INDEXED_ISA
    uint16_t index;
#endif

    explicit_atomic<uintptr_t> ro_or_rw_ext;

    Class firstSubclass;
    Class nextSiblingClass;

	// ...

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

    // ...

    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<constclass_ro_t *>(&ro_or_rw_ext)->baseMethods()}; }}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<constclass_ro_t *>(&ro_or_rw_ext)->baseProperties}; }}// ...
};
Copy the code
  • fromclass_rw_tThe definition of “can be seen in several important ways:
    • methods()
    • properties()
    • ro()
  • The following throughLLDBSo let’s just debug and verify that this is where we think it’s going to be

Read class_rw_t data

  • To getLGPersonThe memory address of the class
  • Obtained by internal translation of the structurebitsvalue
  • To obtainbitsStored in theclass_rw_tType data address
  • To viewclass_rw_tThe specific data stored in

Store of properties (property_t)

  • p $2->properties(): Gets the attribute informationproperty_array_tType data, inherited fromlist_array_tt:
template <typename Element, typename List, template<typename> class Ptr>
class list_array_tt {
	// ...
    union {
        Ptr<List> list;
        uintptr_t arrayAndFlag;
    };
    // ...
};
Copy the code
  • p $8.list.ptr: getproperty_array_tStored in theproperty_list_tdata
    • From the abovelist_array_ttThe definition of theta can be seen herelistforListType data, which is passed inproperty_list_tType data
  • To viewproperty_list_tStoring data,property_list_tInherited fromentsize_list_tt:
    • I’ve defined one heregetMethod, callgetIt will end up callinggetOrEnd, the returned data type isElement
    • ElementThe type is passed inproperty_ttype
template <typename Element, typename List, uint32_t FlagMask, typename PointerModifier = PointerModifierNop>
struct entsize_list_tt {
    uint32_t entsizeAndFlags;
    uint32_t count;
	// ...
    Element& getOrEnd(uint32_t i) const { 
        ASSERT(i <= count);
        return *PointerModifier::modify(*this, (Element *)((uint8_t *)this + sizeof(*this) + i*entsize()));
    }
    Element& get(uint32_t i) const { 
        ASSERT(i < count);
        return getOrEnd(i);
    }
	// ...
};
Copy the code
  • throughgetMethods to obtainproperty_tType data:
struct property_t {
    const char *name;
    const char *attributes;
};
Copy the code

Storage of object methods (method_t)

  • p $2->methods(): Gets method information and returnsmethod_array_tType data, inherited fromlist_array_tt
  • p $12.list.ptr: getmethod_array_tStored in themethod_list_tdata
  • To viewmethod_list_tStoring data,method_list_tInherited fromentsize_list_tt:
    • ElementThe type is passed inmethod_ttype
  • throughgetMethods to obtainmethod_tType data, printed here{}You can take a lookmethod_tDefinition:
    • As you can see by definition,method_tNeed to pass throughbig()Take out the data
struct method_t {
    // ...
    struct big {
        SEL name;
        const char *types;
        MethodListIMP imp;
    };
	// ...
    big &big() const{ ASSERT(! isSmall());return* (struct big *)this;
    }
	// ...
};
Copy the code
  • Print respectivelysayHelloAnd attributesnameThe correspondinggetterandsetterMethods information

Member variable storage (iVAR_t)

  • p $2->ro(): getroInformation, returnclass_ro_tType data,class_ro_tDefinition:
    • This structure containsivar_list_tType of a member variableivars
    • ivarsTo store the member variable data we defined
struct class_ro_t {
    uint32_t flags;
    uint32_t instanceStart;
    uint32_t instanceSize;
#ifdef __LP64__
    uint32_t reserved;
#endif

    union {
        const uint8_t * ivarLayout;
        Class nonMetaclass;
    };

    explicit_atomic<const char *> name;
    // With ptrauth, this is signed if it points to a small list, but
    // may be unsigned if it points to a big list.
    void *baseMethodList;
    protocol_list_t * baseProtocols;
    const ivar_list_t * ivars;

    const uint8_t * weakIvarLayout;
    property_list_t *baseProperties;

    // ...
};
Copy the code
  • p $19->ivars: Gets information about member variables and returnsivar_list_tType data
  • To viewivar_list_tStoring data,ivar_list_tInherited fromentsize_list_tt:
    • ElementThe type is passed inivar_ttype
  • throughgetMethods to obtainivar_tType data:
struct ivar_t {
    int32_t *offset;
    const char *name;
    const char *type;
    // alignment is sometimes -1; use alignment() instead
    uint32_t alignment_raw;
    uint32_t size;
	// ...
};
Copy the code

Storage of class methods (method_t)

  • Class methods are stored in metaclasses

  • throughx/4gx LGPerson.class: seeLGPersonClass memory structure
  • throughISA_MASKGets the value of theLGPersonOf the classisa, that is, to findLGPersonClass corresponding to the metaclass
  • Through the address of the metaclass, memory translation to get the metaclassbitsMemory address
  • throughdata()To obtainbitsData stored in

  • throughmethods()Of the metaclassbitsMethod list data stored in
  • p $12.list.ptr: getmethod_array_tStored in themethod_list_tdata
  • To viewmethod_list_tStoring data,method_list_tInherited fromentsize_list_tt:
    • ElementThe type is passed inmethod_ttype
  • throughgetMethods andbig()To obtainmethod_tType data, outputsay666Method information, that is, class methods, is stored in metaclasses