preface

In the last article we explored where class attributes, methods, and protocols are stored. Today we’ll explore some class member variables

1. Explore the class member variables

In WWDC 2020, here is the structure of the class

It is obvious from the diagram that the class member variables are stored in the class_ro_t structure. So let’s get the class_ro_t structure

1.1 Get the class_ro_t structure of the class

1.2 Why are there methods, attributes, and protocols in both class_ro_t and class_rw_ext_t?

struct class_rw_ext_t {
    DECLARE_AUTHED_PTR_TEMPLATE(class_ro_t)
    class_ro_t_authed_ptr<const class_ro_t> ro;
    method_array_t methods;
    property_array_t properties;
    protocol_array_t protocols;
    char  *demangledName;
    uint32_t version;
};
Copy the code

1.2.1 Clean Memory and Dirty Memory

To answer this question, first know something about what is dirty memory and clean memory

  • clean memory: Indicates the memory that does not change after loading
  • dirty memory: refers to the memory that changes as the process progresses

Once used, the class structure becomes dirty memory because new data is written to it at runtime. For example, we can dynamically add a method or a property through the API provided by Apple.

Dirty memory is more consumed than clean memory because it must always exist as long as the process is running.

Clean Memory allows processes to be removed to save more space. Because if you need clean memory, the system can reload from disk.

In order to save memory, Apple separated class_rw_t into two parts: class_ro_T (clean memory) and class_rw_ext_T (dirty memory)

The methods, properties, and protocols in class_rw_ext are copied from class_ro_t by Apple at runtime. The implementation of the underlying source code is as follows:

class_rw_ext_t *class_rw_t::extAlloc(const class_ro_t *ro, bool deepCopy) { runtimeLock.assertLocked(); auto rwe = objc::zalloc<class_rw_ext_t>(); rwe->version = (ro->flags & RO_META) ? 7:0; method_list_t *list = ro->baseMethods(); if (list) { if (deepCopy) list = list->duplicate(); rwe->methods.attachLists(&list, 1); } // See comments in objc_duplicateClass // property lists and protocol lists historically // have not been deep-copied // This is probably wrong and ought to be fixed some day property_list_t *proplist = ro->baseProperties; if (proplist) { rwe->properties.attachLists(&proplist, 1); } protocol_list_t *protolist = ro->baseProtocols; if (protolist) { rwe->protocols.attachLists(&protolist, 1); } set_ro_or_rwe(rwe, ro); return rwe; }Copy the code

1.3 Getting member variables of a class

Member variables are iVAR_t structures at the bottom

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

1.3 The difference between class attributes and member variables

Class properties = underlined member variable + setter + getter methodSetter and getter methods for properties

static NSString * _I_ZFPerson_nickName(ZFPerson * self, SEL _cmd) { return (*(NSString **)((char *)self + OBJC_IVAR_$_ZFPerson$_nickName)); }
extern "C" __declspec(dllimport) void objc_setProperty (id, SEL, long, id, bool, bool);
static void _I_ZFPerson_setNickName_(ZFPerson * self, SEL _cmd, NSString *nickName) { objc_setProperty (self, _cmd, __OFFSETOFIVAR__(struct ZFPerson, _nickName), (id)nickName, 0, 1); }

static NSInteger _I_ZFPerson_age2(ZFPerson * self, SEL _cmd) { return (*(NSInteger *)((char *)self + OBJC_IVAR_$_ZFPerson$_age2)); }
static void _I_ZFPerson_setAge2_(ZFPerson * self, SEL _cmd, NSInteger age2) { (*(NSInteger *)((char *)self + OBJC_IVAR_$_ZFPerson$_age2)) = age2; }

static NSObject * _I_ZFPerson_obj(ZFPerson * self, SEL _cmd) { return (*(NSObject **)((char *)self + OBJC_IVAR_$_ZFPerson$_obj)); }
static void _I_ZFPerson_setObj_(ZFPerson * self, SEL _cmd, NSObject *obj) { (*(NSObject **)((char *)self + OBJC_IVAR_$_ZFPerson$_obj)) = obj; }
Copy the code

The setters for copy called the objc_setProperty method, and the setters for obj and assign assigned to age2 were assigned to the member variable. The setters for copy called the objc_setProperty method, and the setters for strong called obj and assign to age2 were assigned to the member variable

1.4 Exploring copy modifier setproperty methods (objc_setProperty)

void objc_setProperty(id self, SEL _cmd, ptrdiff_t offset, id newValue, BOOL atomic, signed char shouldCopy) { bool copy = (shouldCopy && shouldCopy ! = MUTABLE_COPY); bool mutableCopy = (shouldCopy == MUTABLE_COPY); reallySetProperty(self, _cmd, newValue, offset, atomic, copy, mutableCopy); } static inline void reallySetProperty(id self, SEL _cmd, id newValue, ptrdiff_t offset, bool atomic, bool copy, bool mutableCopy) { if (offset == 0) { object_setClass(self, newValue); return; } id oldValue; id *slot = (id*) ((char*)self + offset); if (copy) { newValue = [newValue copyWithZone:nil]; } else if (mutableCopy) { newValue = [newValue mutableCopyWithZone:nil]; } else { if (*slot == newValue) return; newValue = objc_retain(newValue); } if (! atomic) { oldValue = *slot; *slot = newValue; } else { spinlock_t& slotlock = PropertyLocks[slot]; slotlock.lock(); oldValue = *slot; *slot = newValue; slotlock.unlock(); } objc_release(oldValue); }Copy the code

Set_objc_setProperty is a process of copying a variable and assigning it to it.