background

  • from64bitsFirst, iOS was introducedTagged PointerTechnology to optimizeNSNumber, NSString, NSDateAnd so on small object storage.

benefits

  • The introduction ofTagged PointerBefore technology,NSNumberThe storage of such objects requires dynamic allocation of memory, maintenance of reference counting, etc.NSNumberPointers to are stored in the heapNSNumberObject address value.

In other words, just to store an integer value of 1, you need to go to the heap to dynamically open up memory, and reference counting to manage that memory, and it costs a total of 24 bytes, don’t you think that’s ridiculous and wasteful?

So, Apple’s dad put the NSNumber(NSString, NSDate) data directly into the pointer! This avoids unnecessary memory overhead and memory management overhead. When Pointers are insufficient to store data, memory is dynamically allocated to store data.

Tagged Pointer = Tag + Data (address + Data)

An 🌰

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        NSNumber *number1 = @1;
        NSNumber *number2 = @2;
        NSNumber *number3 = @999999999999999999;
        
        NSLog(@"number1 = %p", number1);
        NSLog(@"number2 = %p", number2);
        NSLog(@"number3 = %p", number3);
    }

    return 0;
}

2020- 09- 29 17:58:57.252080+0800 MemoryTest[95240:2528802] number1 = 0xdfc933f566855f8e
2020- 09- 29 17:58:57.252517+0800 MemoryTest[95240:2528802] number2 = 0xdfc933f566855fbe
2020- 09- 29 17:58:57.252578+0800 MemoryTest[95240:2528802] number3 = 0x6000039f0080
Copy the code

0xDFC933F566855F8e and 0xDFC933F566855FBe can be seen as a stack address (because the address is large), 0x6000039F0080 is a heap address, Number3 = 999999999999999999 is beyond the scope of 8 bytes bearing, thus became an ordinary pointer.

  • WWDC2013 video Session 404 Advanced in Objective-C shows apple’s introduction to Tagged Pointer features:
  • Tagged Pointer is used to store small objects, such as NSNumber and NSDate

The Tagged Pointer value is no longer an address, but a real value.

  • It’s not really an object anymore, it’s just a normal variable in an object’s skin. Therefore, its memory is not stored in the heap and does not require malloc and free.
  • Three times more efficient at memory reads and 106 times faster at creation.

Thus, Apple’s introduction of Tagged Pointer not only reduces the memory footprint of programs on 64-bit machines, but also improves their running efficiency. It perfectly solves the problems of storage and access efficiency of small memory objects.

View the source code

  • How to determine if a pointer is correctTagged Pointer?

Runtime source objc-internal.h

objc_object::isTaggedPointer() 
{
    return _objc_isTaggedPointer(this);
}

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

We notice the mask _OBJC_TAG_MASK and find its definition:

#if (TARGET_OS_OSX || TARGET_OS_IOSMAC) && __x86_64__ // In iOS
   // 64-bit Mac - tag bit is LSB
#   define OBJC_MSB_TAGGED_POINTERS 0
#else // macOS
   // Everything else - tag bit is MSB
#   define OBJC_MSB_TAGGED_POINTERS 1
#endif

#if OBJC_MSB_TAGGED_POINTERS
#   define _OBJC_TAG_MASK (1UL<<63)
#   define _OBJC_TAG_INDEX_SHIFT 60
#   define _OBJC_TAG_SLOT_SHIFT 60
#   define _OBJC_TAG_PAYLOAD_LSHIFT 4
#   define _OBJC_TAG_PAYLOAD_RSHIFT 4
#   define _OBJC_TAG_EXT_MASK (0xfUL<<60)
#   define _OBJC_TAG_EXT_INDEX_SHIFT 52
#   define _OBJC_TAG_EXT_SLOT_SHIFT 52
#   define _OBJC_TAG_EXT_PAYLOAD_LSHIFT 12
#   define _OBJC_TAG_EXT_PAYLOAD_RSHIFT 12
#else
#   define _OBJC_TAG_MASK 1UL
#   define _OBJC_TAG_INDEX_SHIFT 1
#   define _OBJC_TAG_SLOT_SHIFT 0
#   define _OBJC_TAG_PAYLOAD_LSHIFT 0
#   define _OBJC_TAG_PAYLOAD_RSHIFT 4
#   define _OBJC_TAG_EXT_MASK 0xfUL
#   define _OBJC_TAG_EXT_INDEX_SHIFT 4
#   define _OBJC_TAG_EXT_SLOT_SHIFT 4
#   define _OBJC_TAG_EXT_PAYLOAD_LSHIFT 0
#   define _OBJC_TAG_EXT_PAYLOAD_RSHIFT 12
#endif
Copy the code

Note that the definition of mask _OBJC_TAG_MASK varies from system to system:

On iOS (__x86_64__), _OBJC_TAG_MASK is 1UL<<63, which is equivalent to the most significant bit 1 of the 8-byte address.

On macOS, _OBJC_TAG_MASK is 1UL, equivalent to the least significant bit 1 of an 8-byte address.

((uintptr_t)ptr & _OBJC_TAG_MASK) == _OBJC_TAG_MASK; Perform a bit operation to get the data on the corresponding bit, and check whether it is Tagged Pointer ‘.

  • ubiquitousTagged Pointer

Some runtime runtime source code:

Class object:

// objc-object.h
objc_object::isClass()
{
    if (isTaggedPointer()) return false;
    return ISA() - >isMetaClass(a); }Copy the code

: So Tagged Pointer is no longer an object

inline void
objc_object::rootDealloc(a)
{
    if (isTaggedPointer()) return;  // fixme necessary?

    if (fastpath(isa.nonpointer && ! isa.weakly_referenced && ! isa.has_assoc && ! isa.has_cxx_dtor && ! isa.has_sidetable_rc)) {assert(!sidetable_present());
        free(this);
    } 
    else {
        object_dispose((id)this); }}Copy the code

When an object is released, check whether it is Tagged Pointer first, because Tagged Pointer does not need to be managed by the programmer, the system will reclaim it for you.

inline id 
objc_object::retain(a)
{
    ASSERT(!isTaggedPointer());

    if (fastpath(!ISA() - >hasCustomRR())) {
        return rootRetain(a); }return ((id(*)(objc_object *, SEL))objc_msgSend)(thisThe @selector(retain));
}

objc_release(id obj)
{
    if(! obj)return;
    if (obj->isTaggedPointer()) return;
    return obj->release(a); }Copy the code

: Tagged Pointer Cannot be strongly referenced and there is no reference count.

Refer to the link

www.mikeash.com/pyblog/frid…