Combing the interview

馃惢iOS interview grooming – early August 2020

Turn to c + + code

With the CLang compiler in LLVM:

clang聽-rewrite-objc聽ViewController.m

Copy the code

If you need to export the related system library can be used

clang聽-x聽objective-c聽-rewrite-objc聽-isysroot聽/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk聽ViewController.m

Copy the code

You get a CPP file.

objc1.0

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;

Copy the code
typedef聽struct聽objc_class聽*Class;





struct聽objc_object聽{

聽聽聽聽Class聽_Nonnull聽isa聽__attribute__((deprecated));

};

Copy the code

Typedef Use 1: Type alias, where an objc_class * pointer is declared to be Class

  • isaFor the actualobjc_class * isaOne type isobjc_classThe pointer.

Isa states that the instance object points to the parent class, the class object points to the metaclass, the metaclass points to the root metaclass, and the root metaclass points to itself.

  • super_classFor the actualobjc_class * isaOne type isobjc_classThe pointer.

It points to the parent class, and the parent class points to the parent class, and eventually it points to NSObject

Const is a C keyword that protects a variable from being changed. The const keyword can be used to modify variables, parameters, return values, and even the function body.

  • nameIt’s an immutablecharType, class name.

4 bytes long

  • Version Indicates the version information of the class. The type is long

  • Instance_size Specifies the size of the instance variable. Consider memory alignment

C type Bytes (32 bits) Bytes (64 bits)
char 1 1
short int 2 2
int 4 4
long int 4 8
long long int 8 8
float 4 4
double 8 8
Pointer to the iOS 4 8

Memory alignment rules:

I. The offet of the first variable is 0

2. The storage start unknown must be an integer multiple of the size of the data member. For example, if int is 4 bytes, the storage starts from the address that is an integer multiple of 4.

3. The sizeof a structure must be a multiple of the sizeof its largest member

IOS itself does 16-byte alignment, which means the final size must be an integer multiple of 16.

Person聽*p聽=聽[Person聽alloc];

p.name聽=聽@"Kaemi";聽聽//聽聽NSString聽聽8

p.age聽=聽18;聽聽聽聽聽聽聽聽聽//聽聽int聽聽聽聽聽聽聽4

p.height聽=聽188;聽聽聽聽聽//聽聽long聽聽聽聽聽聽8

p.hobby聽=聽@"game";聽聽//聽聽NSString聽聽8



NSLog(@"Requested memory size: %lu -- system open memory size: %lu",class_getInstanceSize([p聽class]),malloc_size((__bridge聽const聽void聽*)(p)));

Copy the code

Copy the code to print the result: The requested memory size is 40– The allocated memory size is 48

The size of instance_size must be fixed. If it changes dynamically, different objects of the same type will have different instance_size sizes.

Struct objc_iVAR_list * _Nullable ivars

struct聽objc_ivar_list聽{

聽聽聽聽int聽ivar_count聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽OBJC2_UNAVAILABLE;

#ifdef聽__LP64__

聽聽聽聽int聽space聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽OBJC2_UNAVAILABLE;

#endif

聽聽聽聽/*聽variable聽length聽structure聽*/

聽聽聽聽struct聽objc_ivar聽ivar_list[1]聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽OBJC2_UNAVAILABLE;

}

Copy the code

Struct objc_ivar ivar_list[1] struct objc_ivar ivar_list[1

  • ivar_countNumber of variables
  • spaceThe unknown
  • objc_ivarStructure is as follows
struct聽objc_ivar聽{

聽聽聽聽char聽*聽_Nullable聽ivar_name聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽OBJC2_UNAVAILABLE;

聽聽聽聽char聽*聽_Nullable聽ivar_type聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽OBJC2_UNAVAILABLE;

聽聽聽聽int聽ivar_offset聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽OBJC2_UNAVAILABLE;

#ifdef聽__LP64__

聽聽聽聽int聽space聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽OBJC2_UNAVAILABLE;

#endif

}聽

Copy the code

_Nullable means that the object can be NULL or nil, and _Nonnull means that the object should not be NULL

Summary: objc_iVAR_list: indicates an array with variable name iVAR_list and contents of objc_ivar

MethodLists are a nullable pointer of type objC_method_list *

This is a secondary pointer, which is usually used for address-pass-value operations

struct聽objc_method_list聽{

聽聽聽聽struct聽objc_method_list聽*聽_Nullable聽obsolete聽聽聽聽聽聽聽聽聽聽聽聽聽OBJC2_UNAVAILABLE;



聽聽聽聽int聽method_count聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽OBJC2_UNAVAILABLE;

#ifdef聽__LP64__

聽聽聽聽int聽space聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽OBJC2_UNAVAILABLE;

#endif

聽聽聽聽/*聽variable聽length聽structure聽*/

聽聽聽聽struct聽objc_method聽method_list[1]聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽OBJC2_UNAVAILABLE;

}聽

Copy the code
  • objc_method_listThis is obviously a one-way list, so why use oneobjc_method method_list[1]Confused.
typedef聽struct聽objc_method聽*Method;

Copy the code
struct聽objc_method聽{

聽聽聽聽SEL聽_Nonnull聽method_name聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽OBJC2_UNAVAILABLE;

聽聽聽聽char聽*聽_Nullable聽method_types聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽OBJC2_UNAVAILABLE;

聽聽聽聽IMP聽_Nonnull聽method_imp聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽OBJC2_UNAVAILABLE;

}聽

Copy the code
  • Method_types, which is a char pointer that stores the method’s parameter type and return value type

SEL method name IMP function pointer

Cache is a pointer to objc_cache

typedef聽struct聽objc_cache聽*Cache聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽OBJC2_UNAVAILABLE;



struct聽objc_cache聽{

聽聽聽聽unsigned聽int聽mask聽/*聽total聽=聽mask聽+聽1聽*/聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽OBJC2_UNAVAILABLE;

聽聽聽聽unsigned聽int聽occupied聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽OBJC2_UNAVAILABLE;

聽聽聽聽Method聽_Nullable聽buckets[1]聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽OBJC2_UNAVAILABLE;

};

Copy the code

Mask Specifies the number of buckets to be allocated. Occupied Number of buckets to be allocated. The number of buckets is smaller than mask+1 = total

Protocols is a pointer of type objc_PROTOCOL_list

#ifdef聽__OBJC__

@class聽Protocol;

#else

typedef聽struct聽objc_object聽Protocol;

#endif

Copy the code
struct聽objc_protocol_list聽{

聽聽聽聽struct聽objc_protocol_list聽*聽_Nullable聽next;

聽聽聽聽long聽count;

聽聽聽聽__unsafe_unretained聽Protocol聽*聽_Nullable聽list[1];

};



Copy the code

Objc_class ends this analysis.

Reanalyze the Category

typedef聽struct聽objc_category聽*Category;

Copy the code
struct聽objc_category聽{

聽聽聽聽char聽*聽_Nonnull聽category_name聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽OBJC2_UNAVAILABLE;

聽聽聽聽char聽*聽_Nonnull聽class_name聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽OBJC2_UNAVAILABLE;

聽聽聽聽struct聽objc_method_list聽*聽_Nullable聽instance_methods聽聽聽聽聽OBJC2_UNAVAILABLE;

聽聽聽聽struct聽objc_method_list聽*聽_Nullable聽class_methods聽聽聽聽聽聽聽聽OBJC2_UNAVAILABLE;

聽聽聽聽struct聽objc_protocol_list聽*聽_Nullable聽protocols聽聽聽聽聽聽聽聽聽聽OBJC2_UNAVAILABLE;

}

Copy the code

Category_name: category name class_name: class name

The others are the same as objc_class, and we see that there is no ivars.

objc2.0

typedef struct objc_class *Class;
typedef struct objc_object *id;
Copy the code
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(); // Other methods declare... }Copy the code
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(); } // Other methods declare... }Copy the code

Objc_class inherits objc_Object, and in ObjC2.0 a class is an object

  • Isa is of type ISA_t and its structure is:
union isa_t { isa_t() { } isa_t(uintptr_t value) : bits(value) { } Class cls; uintptr_t bits; # if __arm64__ # define ISA_MASK 0x0000000ffffffff8ULL # define ISA_MAGIC_MASK 0x000003f000000001ULL # define ISA_MAGIC_VALUE 0x000001a000000001ULL struct { uintptr_t nonpointer : 1 ; Uintptr_t has_ASsoc: 1; Uintptr_t has_cxx_dtor:1; Uintptr_t shiftcls: 33; Class pointer; Uintptr_t magic: 6; Fixed value 0xd2, used during debugging to tell if an object has not been initialized. Uintptr_t weakly_referenced: 1; Uintptr_t dealLocating :1; Uintptr_t has_sidetable_rc:1; Object reference count is too large, Uintptr_t extra_rc:19 reference count} # define RC_ONE (1ULL<<45) # define RC_HALF (1ULL<<18) # elif __x86_64__ // 路路路路 路 # endif}; };Copy the code

Structures in ISA are bit-field declared, 8 bytes in total, 64 bits. Isa: The instance object points to the parent, the class object points to the metaclass, the metaclass points to the root metaclass, and the root metaclass points to itself.

  • The rules are the same in SuperClass and Object C1.0

  • Cache_t Cache method The cache cache_t structure is as follows:

struct cache_t { struct bucket_t *_buckets; mask_t _mask; mask_t _occupied; // Other methods declare... }Copy the code

bucket_t *_buckets; Structure of bucket_t:

struct bucket_t { private: // IMP-first is better for arm64e ptrauth and no worse for arm64. // SEL-first is better for armv7* and i386 and x86_64.  #if __arm64__ MethodCacheIMP _imp; cache_key_t _key; #else cache_key_t _key; MethodCacheIMP _imp; #endif // Other statements... }Copy the code

Bucket_t is a hash table with key cache_KEY_t and _IMP MethodCacheIMP

  • Bits is of the typeclass_data_bits_t
struct class_data_bits_t { // Values are the FAST_ flags above. uintptr_t bits; public: class_rw_t *data() { return (class_rw_t *)(bits & FAST_DATA_MASK); } // Other definitions... }Copy the code

Class_data_bits_t has a class_rw_t; The definition is as follows:

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; // Other definitions... }Copy the code

We see class_ro_t that has a const modifier in it and let’s see class_ro_t

struct class_ro_t { uint32_t flags; uint32_t instanceStart; uint32_t instanceSize; #ifdef __LP64__ uint32_t reserved; #endif const uint8_t * ivarLayout; const char * name; method_list_t * baseMethodList; protocol_list_t * baseProtocols; const ivar_list_t * ivars; const uint8_t * weakIvarLayout; property_list_t *baseProperties; method_list_t *baseMethods() const { return baseMethodList; }};Copy the code
  • class_ro-tIs a pointer to a constant that stores compiler-determined variables, methods, protocols, properties, and so on
  • class_rw_tProvides the ability to extend classes at runtime.

Both have class methods, attributes, member variables, and protocols, but store different structures

  • ivar_t
struct ivar_t { #if __x86_64__ // *offset was originally 64-bit on some x86_64 platforms. // We read and write only 32 bits of it. // Some metadata provides all 64 bits. This is harmless for unsigned // little-endian values. // Some code uses all 64 bits. class_addIvar() over-allocates the // offset for their benefit. #endif int32_t *offset; const char *name; const char *type; // alignment is sometimes -1; use alignment() instead uint32_t alignment_raw; uint32_t size; uint32_t alignment() const { if (alignment_raw == ~(uint32_t)0) return 1U << WORD_SHIFT; return 1 << alignment_raw; }};Copy the code

Property, member variable name, type, memory offset, size

  • Category
#if __OBJC2__
typedef struct category_t *Category;
Copy the code
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);
};
Copy the code

Category_t stores instance methods, class methods, protocols, instance attributes, and class attributes that can be extended within the classification. When the App starts, the Runtime adds categories to the class by calling attachCategories after loading the class. Add method_list_t, property_array_t, and protocol_array_T to class_rw_t, respectively. Protocol_list_t pointer

  • Protocol
struct protocol_t : objc_object { const char *mangledName; struct protocol_list_t *protocols; method_list_t *instanceMethods; method_list_t *classMethods; method_list_t *optionalInstanceMethods; method_list_t *optionalClassMethods; property_list_t *instanceProperties; uint32_t size; // sizeof(protocol_t) uint32_t flags; // Fields below this point are not always present on disk. const char **_extendedMethodTypes; const char *_demangledName; property_list_t *_classProperties; // Other definitions... }Copy the code

Protocol_t inherits from objC_Object, which defines instance methods, class methods, optional instance methods, optional class methods, instance properties, and class properties declared in the protocol. In addition, because Swift also supports Protocol multiple inheritance, you need the Protocols array for compatibility.

struct protocol_list_t { // count is 64-bit by accident. uintptr_t count; protocol_ref_t list[0]; // variable size // other definitions... }Copy the code