iOS memory management mechanism and principle

memory partition


Memory is generally divided into five areas: stack area, heap area, constant area, global area, and code area. as shown in the picture

1. Stack area

It is automatically allocated and released by the compiler. It is mainly used to store local variables and function parameters. It is a continuous memory area and follows the first-in-last-out (FILO) principle. Typically allocated at runtime. Its allocation is allocated from high address space to low address space.

Advantages: Because the stack is automatically allocated and released by the compiler, there will be no memory fragmentation, so it is fast and efficient.
Disadvantages: The memory size of the stack is limited, and the data is not flexible.

For example: In the figure below, two variables are created and stored in the stack area, and the address is decremented by 4.

2. Heap area

The heap is managed manually by the programmer. Mainly used to store dynamically allocated object type data. is a non-contiguous area of ​​memory. In the MRC environment, it is manually released by the programmer, and in the ARC environment, it is automatically released by the compiler. Typically allocated at runtime. Its allocation is from low address space to high address space.
Advantages: flexible and convenient, wide range of data adaptation.
Disadvantages: manual management is required, the speed is slow, and memory fragmentation is prone to occur.

For example: in the figure below, two objects are created, stored in the heap area, and the address is incremented.

 

3. Constant area

The constant area stores string constants and basic type constants . Allocated at compile time and reclaimed at the end of the program

4. Global zone/static zone

Global variables and static variables are stored together. Initialized global variables and static variables are stored in .datasegments, and uninitialized global variables and static variables are in adjacent .bssareas. They are allocated at compile time and released by the system after the program ends.

5. Code area

The code area is allocated at compile time . It is mainly used to store the code when the program is running. The code will be compiled into binary and stored in memory.

memory management


 memory management scheme

1.TaggedPointer

In September 2013, Apple launched the first mobile phone iPhone5s with A7 dual-core processor using 64-bit architecture. In order to improve the memory waste and efficiency problems of migrating from 32-bit CPUto 64-bit, in the 64-bit environment, Apple engineers proposed the Tagged Pointerconcept of . Using this mechanism, the system optimizes objects such as NSString, NSNumberand . NSDateI suggest you watch the introduction of this video of WWDC2020.

Tagged Pointer is specially used to store small objects, such as NSNumber, NSDate, etc. The value of Tagged Pointer pointer is no longer a simple address, but a real value, so, in fact, it is no longer an object, it is just a It is just an ordinary variable in the skin of an object, so its memory is not stored in the heap area, and malloc and free are not required. This has 3 times the efficiency of reading, and the creation time is 106 times faster than before.

It can be seen from the above figure that NSdate is NSTaggedPointer,In addition, when the length of the string is less than 10, the type of the string is NSTaggedPointerStringtype, when it exceeds 10, the type of the string is__NSCFString

 NSTaggedPointer flag bit

static inline bool 
_objc_isTaggedPointer(const void * _Nullable ptr)
{
    return ((uintptr_t)ptr & _OBJC_TAG_MASK) == _OBJC_TAG_MASK;
}

In the above method, we can see that judging whether an object type is NSTaggedPointerStringa type actually refers to the address of the object and _OBJC_TAG_MASKperforms a bitwise AND operation. The result _OBJC_TAG_MASKis compared with the following _OBJC_TAG_MASKdefinition:

#if OBJC_MSB_TAGGED_POINTERS
#   define _OBJC_TAG_MASK (1UL<<63)
#else
#   define _OBJC_TAG_MASK 1UL
#endif

We all know that an object address is a 64-bit binary, which indicates that if the highest bit in the 64-bit data is 1, it indicates that it is currently a tagged pointertype.

Then we look at the addresses printed above, all NSTaggedPointerStringaddresses are 0xdat the beginning, and d is converted to binary 1110. According to the above conclusion, we see that the first bit is 1 to indicate NSTaggedPointerStringthe type. Get verified here.

注意: TaggedPointerType flags are different in iOS and MacOS, iOS is the highest bit and MacOS is the lowest bit

object type

Under normal circumstances, the type of an object is judged by the ISA pointer of the object, so for NSTaggedPointerthe type, how do we judge the type of the corresponding data through the address?

Before objc4-723, we can TaggedPointerjudge according to the address as judging the flag bit, and the type flag bit is the 61-63 bit of the object address. For example, if the object address starts with a 0xabinary bit 1010, then remove the highest bit flag After digits, the remainder 010is 2 in decimal.

Next, let's look at the definitions of the flag bits in the runtime source code objc-internal.h as follows:

#if __has_feature(objc_fixed_enum)  ||  __cplusplus >= 201103L
enum objc_tag_index_t : uint16_t
#else
typedef uint16_t objc_tag_index_t;
enum
#endif
{
    // 60-bit payloads
    OBJC_TAG_NSAtom            = 0, 
    OBJC_TAG_1                 = 1, 
    OBJC_TAG_NSString          = 2, 
    OBJC_TAG_NSNumber          = 3, 
    OBJC_TAG_NSIndexPath       = 4, 
    OBJC_TAG_NSManagedObjectID = 5, 
    OBJC_TAG_NSDate            = 6,

    // 60-bit reserved
    OBJC_TAG_RESERVED_7        = 7, 

    // 52-bit payloads
    OBJC_TAG_Photos_1          = 8,
    OBJC_TAG_Photos_2          = 9,
    OBJC_TAG_Photos_3          = 10,
    OBJC_TAG_Photos_4          = 11,
    OBJC_TAG_XPC_1             = 12,
    OBJC_TAG_XPC_2             = 13,
    OBJC_TAG_XPC_3             = 14,
    OBJC_TAG_XPC_4             = 15,

    OBJC_TAG_First60BitPayload = 0, 
    OBJC_TAG_Last60BitPayload  = 6, 
    OBJC_TAG_First52BitPayload = 8, 
    OBJC_TAG_Last52BitPayload  = 263, 

    OBJC_TAG_RESERVED_264      = 264
};
#if __has_feature(objc_fixed_enum)  &&  !defined(__cplusplus)
typedef enum objc_tag_index_t objc_tag_index_t;
#endif

after objc4-750

// Returns a pointer to the class's storage in the tagged class arrays.
// Assumes the tag is a valid basic tag.
static Class *
classSlotForBasicTagIndex(objc_tag_index_t tag)
{
    uintptr_t tagObfuscator = ((objc_debug_taggedpointer_obfuscator
                                >> _OBJC_TAG_INDEX_SHIFT)
                               & _OBJC_TAG_INDEX_MASK);
    uintptr_t obfuscatedTag = tag ^ tagObfuscator;
    // Array index in objc_tag_classes includes the tagged bit itself
#if SUPPORT_MSB_TAGGED_POINTERS 高位优先
    return &objc_tag_classes[0x8 | obfuscatedTag];
#else
    return &objc_tag_classes[(obfuscatedTag << 1) | 1];
#endif
}

 The main function of the classSlotForBasicTagIndex() function is to obtain the class pointer from the array objc_tag_classes according to the specified index tag, and the calculation method of the subscript is based on the externally passed index tag. For example the string tag = 2. Of course, this is not simply to get a piece of data from the array.

Get the value of TaggedPointer

Source code after objc4-750:

static inline uintptr_t
_objc_decodeTaggedPointer(const void * _Nullable ptr)
{
    return (uintptr_t)ptr ^ objc_debug_taggedpointer_obfuscator;
}

static inline uintptr_t
_objc_getTaggedPointerValue(const void * _Nullable ptr) 
{
    // assert(_objc_isTaggedPointer(ptr));
    uintptr_t value = _objc_decodeTaggedPointer(ptr);
    uintptr_t basicTag = (value >> _OBJC_TAG_INDEX_SHIFT) & _OBJC_TAG_INDEX_MASK;
    if (basicTag == _OBJC_TAG_INDEX_MASK) {
        return (value << _OBJC_TAG_EXT_PAYLOAD_LSHIFT) >> _OBJC_TAG_EXT_PAYLOAD_RSHIFT;
    } else {
        return (value << _OBJC_TAG_PAYLOAD_LSHIFT) >> _OBJC_TAG_PAYLOAD_RSHIFT;
    }
}

static inline intptr_t
_objc_getTaggedPointerSignedValue(const void * _Nullable ptr) 
{
    // assert(_objc_isTaggedPointer(ptr));
    uintptr_t value = _objc_decodeTaggedPointer(ptr);
    uintptr_t basicTag = (value >> _OBJC_TAG_INDEX_SHIFT) & _OBJC_TAG_INDEX_MASK;
    if (basicTag == _OBJC_TAG_INDEX_MASK) {
        return ((intptr_t)value << _OBJC_TAG_EXT_PAYLOAD_LSHIFT) >> _OBJC_TAG_EXT_PAYLOAD_RSHIFT;
    } else {
        return ((intptr_t)value << _OBJC_TAG_PAYLOAD_LSHIFT) >> _OBJC_TAG_PAYLOAD_RSHIFT;
    }
}

Example code and results

  NSString *str1 = [NSString stringWithFormat:@"1"];
            NSString *str11 = [NSString stringWithFormat:@"11"];
            NSString *str2 = [NSString stringWithFormat:@"2"];
            NSString *str22 = [NSString stringWithFormat:@"22"];


            // 0x31 1 0x32 1
            uintptr_t value1 = objc_getTaggedPointerValue((__bridge void *)str1);
            uintptr_t value2 = objc_getTaggedPointerValue((__bridge void *)str2);
            uintptr_t value11 = objc_getTaggedPointerValue((__bridge void *)str11);
            uintptr_t value22 = objc_getTaggedPointerValue((__bridge void *)str22);
            // 以16进制形式输出
            NSLog(@"%lx", value1);
            NSLog(@"%lx", value11);
            NSLog(@"%lx", value2);
            NSLog(@"%lx", value22);
TaggedPointer[89535:3033433] 311
TaggedPointer[89535:3033433] 31312
TaggedPointer[89535:3033433] 321
TaggedPointer[89535:3033433] 32322

The TaggedPoint object is a special object that does not involve reference counting retain, releasememory operations, etc. The value of the object is stored in the pointer, but the value is encrypted.

2.NONPOINTER_ISA

As we said above, Apple has optimized the storage of an object, so what about ISA pointers?

The isa pointer of the object is used to indicate the class type to which the object belongs.

union isa_t {
    isa_t() { }
    isa_t(uintptr_t value) : bits(value) { }

    Class cls;
    uintptr_t bits;
#if defined(ISA_BITFIELD)
    struct {
        ISA_BITFIELD;  // defined in isa.h
    };
#endif
};

Combined with the following figure

It can be seen from the figure that our so-called isa pointer actually ends up in the joint type of isa_t. So what is a union type? 联合类型是C语言中的一种类型,是一种n选1的关系,联合的作用在于,用更少的空间,表示了更多的可能的类型,虽然这些类型是不能够共存的. clsFor example , isa_t contains bitsthree structvariables, and their memory spaces overlap. In actual use, only one of them can be used. If you treat it as cls, you cannot access it as bits, and if you treat it as bits, you cannot use cls to access it.

For isa_tthe joint type, it mainly includes two constructors isa_t()and isa_t(uintptr_t value)three variables cls, bits, struct, and uintptr_tis defined as typedef unsigned long.

When isa_t is used as Class cls, this conforms to our previous cognition: isa is a pointer to the Class type to which the object belongs. However, it is obviously not cost-effective to only let a 64-bit pointer represent a type.

Therefore, in the vast majority of cases, Apple uses optimized isa策略, that is, isa_tthe types are not equivalent Class clsbut rather struct.

Let's first look at the structure of struct

// ISA_BITFIELD定义如下
# if __arm64__
#   define ISA_MASK        0x0000000ffffffff8ULL
#   define ISA_MAGIC_MASK  0x000003f000000001ULL
#   define ISA_MAGIC_VALUE 0x000001a000000001ULL
#   define ISA_BITFIELD                                                      \
      uintptr_t nonpointer        : 1;                                       \
      uintptr_t has_assoc         : 1;                                       \
      uintptr_t has_cxx_dtor      : 1;                                       \
      uintptr_t shiftcls          : 33; /*MACH_VM_MAX_ADDRESS 0x1000000000*/ \
      uintptr_t magic             : 6;                                       \
      uintptr_t weakly_referenced : 1;                                       \
      uintptr_t deallocating      : 1;                                       \
      uintptr_t has_sidetable_rc  : 1;                                       \
      uintptr_t extra_rc          : 19
#   define RC_ONE   (1ULL<<45)
#   define RC_HALF  (1ULL<<18)

Note: 成员后面的:表明了该成员占用几个bitThe meaning of each member is as follows

nonopointer: Indicates whether pointer optimization is enabled for the isa pointer,

0: pure isa pointer, 1: not only the address of the class object, isa contains class information, reference count of the object, etc.

has_assoc: associated object flag, 0: no, 1: exists

has_cxx_dtor: Whether the object has a c++ or Objc destructor. If there is a destructor function, the destructor logic needs to be done. If not, it can be released faster.

 shiftcls: Stores the value of the pointer. When the pointer optimization is turned on, there are 33 bits in the arm64 architecture to store the class pointer.

 magic: Used by the debugger to determine whether the current object is a real object or an uninitialized space.

weakly_referenced: Whether the Peugeot object points to or once pointed to an ARC variable. Objects without weak references can be released faster.

dealcating: whether the flag object is releasing memory

has_sidetable_rc: When the current object reference count is greater than 10, you need to borrow the variable to store the carry

extra_rc: When representing the reference count value of the object, it is actually the reference count value minus 1. For example, if the reference count of the object is 10, then extra_rc is 9. If the count is greater than 10, you need to use the following has_sidetable_rc.

3. Hash table, reference count table

Sidetable mainly includes spinlock, reference count (stores the other half of the reference count received by extra_rc), and weak reference table.

truct SideTable {
    spinlock_t slock;
    // 存放从extra_rc接收的那一半引用计数
    RefcountMap refcnts;
    // 弱引用表
    weak_table_t weak_table;

    SideTable() {
        memset(&weak_table, 0, sizeof(weak_table));
    }

    ~SideTable() {
        _objc_fatal("Do not delete SideTable.");
    }

    void lock() { slock.lock(); }
    void unlock() { slock.unlock(); }
    void forceReset() { slock.forceReset(); }

    // Address-ordered lock discipline for a pair of side tables.

    template<HaveOld, HaveNew>
    static void lockTwo(SideTable *lock1, SideTable *lock2);
    template<HaveOld, HaveNew>
    static void unlockTwo(SideTable *lock1, SideTable *lock2);
};

1.spinlock_t lock

spinlock_t is not a spin lock. In the process of searching the underlying code, we can find that it is an os_unfair_lock lock. When using sidetable, frequent reading needs to be locked. A table will undoubtedly affect the efficiency. Therefore, we use stripedMap to disperse the pressure, and the number of stripedMaps is determined according to the system (the maximum number of sidetables in real machine mode is 8, and the number of virtual machines is 64).

// 上面 SideTables 的实现
static StripedMap<SideTable>& SideTables() {
    return SideTablesMap.get();
}

2.RefcountMap (reference counting table)

  • RefcountMap itself is derived from DenseMap
    typedef objc::DenseMap<DisguisedPtr<objc_object>,size_t,RefcountMapValuePurgeable> RefcountMap;
    

Holds the half of the reference count received from extra_rc

if (variant == RRVariant::Full) {
    if (slowpath(transcribeToSideTable)) {
        // Copy the other half of the retain counts to the side table.
        // 将引用计数一半存在散列表中的方法
        sidetable_addExtraRC_nolock(RC_HALF);
    }
    if (slowpath(!tryRetain && sideTableLocked)) sidetable_unlock();
} else {
    ASSERT(!transcribeToSideTable);
    ASSERT(!sideTableLocked);
}

Next, take a look at the sidetable_addExtraRC_nolock method:

bool 
objc_object::sidetable_addExtraRC_nolock(size_t delta_rc)
{
    ASSERT(isa.nonpointer);
    // 获取SideTables,也就是StripeMap
    SideTable& table = SideTables()[this];

    size_t& refcntStorage = table.refcnts[this];
    size_t oldRefcnt = refcntStorage;
    // isa-side bits should not be set here
    ASSERT((oldRefcnt & SIDE_TABLE_DEALLOCATING) == 0);
    ASSERT((oldRefcnt & SIDE_TABLE_WEAKLY_REFERENCED) == 0);

    if (oldRefcnt & SIDE_TABLE_RC_PINNED) return true;

    uintptr_t carry;
    size_t newRefcnt = 
        addc(oldRefcnt, delta_rc << SIDE_TABLE_RC_SHIFT, 0, &carry);
    if (carry) {
        refcntStorage =
            SIDE_TABLE_RC_PINNED | (oldRefcnt & SIDE_TABLE_FLAG_MASK);
        return true;
    }
    else {
        refcntStorage = newRefcnt;
        return false;
    }
}

3. WeakTable (weak reference table)

Weak reference underlying calls objc_initWeak:

id objc_initWeak(id *location, id newObj)
{
    if (!newObj) {
        *location = nil;
        return nil;
    }

    return storeWeak<DontHaveOld, DoHaveNew, DoCrashIfDeallocating>
        (location, (objc_object*)newObj);
}

storeWeak:

  • When adding a reference, calling storeWeak has a total of five parameters, 3 of which are defined in the template template parameter (HaveOld: whether the weak pointer points to a weak reference; HavNew: whether the weak pointer needs to point to a new reference; crashIfDeallocating indicates that the weak whether the referenced object is being destructed).
  • weak_unregister_no_lock: Clear the data in the original weak reference table
  • weak_register_no_lock: Add the weak pointer address to the object's weak reference table
enum CrashIfDeallocating {
    DontCrashIfDeallocating = false, DoCrashIfDeallocating = true
};

// HaveOld:weak指针是否指向一个弱引用
// HavNew:weak指针是否需要指向一个新的引用
template <HaveOld haveOld, HaveNew haveNew,
          enum CrashIfDeallocating crashIfDeallocating>
static id 
storeWeak(id *location, objc_object *newObj)
{
    ASSERT(haveOld  ||  haveNew);
    if (!haveNew) ASSERT(newObj == nil);

    Class previouslyInitializedClass = nil;
    id oldObj;
    SideTable *oldTable;
    SideTable *newTable;

    // Acquire locks for old and new values.
    // Order by lock address to prevent lock ordering problems. 
    // Retry if the old value changes underneath us.
 retry:
    if (haveOld) {//如果有拿到旧表
        oldObj = *location;
        oldTable = &SideTables()[oldObj];
    } else {
        oldTable = nil;
    }
    if (haveNew) {//如果没有创建新表
        newTable = &SideTables()[newObj];
    } else {
        newTable = nil;
    }

    SideTable::lockTwo<haveOld, haveNew>(oldTable, newTable);

    if (haveOld  &&  *location != oldObj) {//如果旧表不存在对应的obj
        SideTable::unlockTwo<haveOld, haveNew>(oldTable, newTable);
        goto retry;
    }

    // Prevent a deadlock between the weak reference machinery
    // and the +initialize machinery by ensuring that no 
    // weakly-referenced object has an un-+initialized isa.
    if (haveNew  &&  newObj) {//有新表和新对象
        Class cls = newObj->getIsa();
        if (cls != previouslyInitializedClass  &&  
            !((objc_class *)cls)->isInitialized()) //如果类没有初始化就重新初始化
        {
            SideTable::unlockTwo<haveOld, haveNew>(oldTable, newTable);
            class_initialize(cls, (id)newObj);

            // If this class is finished with +initialize then we're good.
            // If this class is still running +initialize on this thread 
            // (i.e. +initialize called storeWeak on an instance of itself)
            // then we may proceed but it will appear initializing and 
            // not yet initialized to the check above.
            // Instead set previouslyInitializedClass to recognize it on retry.
            previouslyInitializedClass = cls;

            goto retry;
        }
    }

    // Clean up old value, if any.
    if (haveOld) {//如果指针曾经指向别的对象,就清除
        // 清除原来弱引用表中数据
        weak_unregister_no_lock(&oldTable->weak_table, oldObj, location);
    }

    // Assign new value, if any.
    if (haveNew) {
        newObj = (objc_object *)
            // 将weak的指针地址添加到对象的弱引用表
            weak_register_no_lock(&newTable->weak_table, (id)newObj, location, 
                                  crashIfDeallocating ? CrashIfDeallocating : ReturnNilIfDeallocating);
        // weak_register_no_lock returns nil if weak store should be rejected

        // Set is-weakly-referenced bit in refcount table.
        if (!newObj->isTaggedPointerOrNil()) {
            // 将对象曾经指向过弱引用的标识置为true,没有弱引用的释放更快
            newObj->setWeaklyReferenced_nolock();
        }

        // Do not set *location anywhere else. That would introduce a race.
        *location = (id)newObj;
    }
    else {
        // No new value. The storage is not changed.
    }

    SideTable::unlockTwo<haveOld, haveNew>(oldTable, newTable);

    // This must be called without the locks held, as it can invoke
    // arbitrary code. In particular, even if _setWeaklyReferenced
    // is not implemented, resolveInstanceMethod: may be, and may
    // call back into the weak reference machinery.
    callSetWeaklyReferenced((id)newObj);

    return (id)newObj;
}

weak_entry_t:

struct weak_table_t {
    // 弱引用数组
    weak_entry_t *weak_entries;
    // 数组个数
    size_t    num_entries;
    uintptr_t mask;
    uintptr_t max_hash_displacement;
};

weak_entries is a hash array. An object can be referenced by multiple weak reference pointers. Therefore, multiple weak references of an object are represented in the form of an array; the content stored in the array is the pointer address of the weak reference object. When the number of weak references of the object is less than or equal to 4, use static storage (allocated together when weak_entry_t is initialized), and if it is greater than 4, use dynamic storage.

sidetables summary:

  • sidetables can be understood as a global hash array, which stores sidetables type data, the length of which is 8 or 64
  • An obj (oc object) corresponds to a sideTable, but a SideTable will correspond to multiple objs, because the number of sidetabels is only 8 or 64, so many objs will share a sidetable
  • In the weak reference table, the key is the address of the object, and the value is an array of weak pointer addresses (weak_entry_t)
  • Both weak_unregister_no_lock and weak_register_no_lock operate on arrays of type weak_entry_t
  • _ _weak modified object (will not be put into the automatic release pool), will call objc_loadWeakRetained; make the reference count increase by one, but it is only a temporary variable; the referenced object will not increase its reference count.

 

 ARC&MRC


Objective-C provides two memory management mechanisms MRC (Mannul Reference Counting) and ARC (Automatic Reference Counting), which provides manual and automatic memory management for Objective-C.

MRC

Basic idea: memory management of objects through manual reference counting

method involved

  1. alloc/new/copy/mutableCopy: generate objects and hold them by themselves, reference count +1 (from 0 to 1)
  2. retain: hold the object, increase the reference count of the object by 1
  3. release : Release the object and decrement the reference count of the object by 1
  4. retainCount : Get the reference count value of the current object
  5. autorelease: The current object will call the release operation of this object when the autoreleasePool ends, and the reference count will be decremented by 1
  6. dealloc : If you call dealloc in MRC, you need to explicitly call [super dealloc] to release the related member variables of the parent class

autorelease

Autorelease is "automatic release", which is an automatic memory recovery mechanism of OC, which can recycle some temporary variables through the automatic release pool for unified release. When the automatic release pool is destroyed, all objects in the pool will perform a release operation

So, what is the difference between autorelease release and simple release release?

Calling the autorelease method will put the object into the nearest autorelease pool (the release pool on the top of the stack, and the nesting of multiple autorelease pools is accessed in the form of a stack), that is: the object's ownership Transferred to the automatic release pool (that is, registered in the automatic release pool), the caller got the object, but the object is not yet held by the caller. When the autorelease pool is destroyed, all objects in it will call a release operation.

In essence, the difference is that the autorelease method does not change the reference count of the caller, it just changes the timing of object release, and no longer makes the programmer responsible for releasing the object, but hands it over to the autorelease pool for processing.

 The autorelease method is equivalent to registering the caller in the autoreleasepool. In the ARC environment, the autorelease method cannot be explicitly called and the NSAutoreleasePool object can be explicitly created, but the @autoreleasepool { } block can be used instead (it does not mean that all content in the block is registered into the autorelease pool).
For all objects that have called the autorelease instance method, the release instance method will be called when the NSAutoreleasePool object is discarded.

RunLoop and AutoReleasePool are one-to-one correspondence through threads
. In the non-manually added Autorelease pool, the Autorelease object is released before the current runloop enters sleep and waits
. When a runloop is working in a non-stop cycle, then each runloop cycle must be After BeforeWaiting (ready to enter hibernation): When going to BeforeWaiting (prepared to enter hibernation), _objc_autoreleasePoolPop() and _objc_autoreleasePoolPush() will be called to release the old pool and create a new pool, then these two methods will destroy the object to be released, so We don't need to worry about Autorelease's memory management at all.

ARC

memory management scheme

iOS memory management solutions are:

  1. MRC and ARC
  2. Tagged Pointer: Specially used to deal with small objects, such as NSNumber, NSDate, small NSString, etc.
  3. NONPOINTER_ISA : non-pointer type isa, mainly used to optimize 64-bit addresses. Under the 64-bit architecture, the isa pointer occupies 64 bits. In fact, only more than 30 bits are enough. In order to improve the utilization rate, the remaining bits store the relevant data content of memory management.
  4. nonpointer: Indicates whether to enable pointer optimization for isa pointers
  5. • 0: pure isa pointer
  6. • 1: Not only the address of the class object, isa contains class information, reference count of the object, etc.
  7. SideTables: hash table, there are two main tables in the hash table, namely the reference count table and the weak reference table. It is realized through the SideTables() structure. Under the SideTables() structure, there are many SideTable data structures. The sideTable contains spin locks, reference counting tables, and weak reference tables. SideTables() is actually a hash table, which calculates which sideTable the reference count of the object is in through the address of the object.
     

Modifier

When ARC is valid, the id type and the object type must have ownership modifiers, and there are four types as follows.

  • __strong
  • __weak
  • __unsafe_unretained
  • __autoreleasing

__strong modifier

The __strong modifier is the default ownership modifier for id types and object types.

__weak modifier

A weak reference means that the object is not held. When the referenced object is destroyed, the variable is automatically set to nil.
You can use the __weak modifier to resolve circular references.

__unsafe_unretained modifier

__unsafe_unretained is very similar to __weak, the only difference is that after the object referenced by the __unsafe_unretained variable is destroyed, it will not be automatically set to nil, and it still points to the memory address before the object is destroyed. So its name is unsafe. At this time, if you try to access the properties or methods of this object through variables, it will crash. Once the object is released, it will become a dangling pointer and the program will crash. Therefore, the variable with the __unnsafe_unretained modifier must be used when the assigned object exists.
 

__autoreleasing modifier

Use autorelease when ARC is invalid, and use __autorelease under ARC:

@autoreleasepool {
	id __autoreleasing obj = [[NSObject alloc] init];
}

Guess you like

Origin blog.csdn.net/wywinstonwy/article/details/129949464