美文网首页
OC底层探索27-TaggedPoint

OC底层探索27-TaggedPoint

作者: Henry________ | 来源:发表于2021-07-12 22:15 被阅读0次

在objc的源码分析中,经常看到这个isTaggedPointer判断,这个TaggedPoint有什么特别呢?

1、 TaggedPointer简介

taggedPonint
  • Tagged Pointer专门用来存储小的对象,例如NSNumber, NSDate, NSString。
  • Tagged Pointer指针的值不再是地址了,而是真正的值。所以,实际上它不再是一个对象了,它只是一个披着对象皮的普通变量而已。所以,它的内存并不存储在堆中,也不需要malloc和free。(在常量区)
  • 在内存读取上有着3倍的效率,创建时比以前快106倍。

1.1 TaggedPointer标示

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

static inline bool 
_objc_isTaggedPointer(const void * _Nullable ptr)
{
    return ((uintptr_t)ptr & _OBJC_TAG_MASK) == _OBJC_TAG_MASK;
}
  • 我们都知道一个对象地址为64位二进制,它表明如果64位数据中,最高位是1的话,则表明当前是一个tagged pointer类型

1.2 对象的加密&解密

static inline void * _Nonnull
_objc_encodeTaggedPointer(uintptr_t ptr)
{
    uintptr_t value = (objc_debug_taggedpointer_obfuscator ^ ptr);
    return (void *)value;
}

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

  • objc_debug_taggedpointer_obfuscator这个值苹果源码中并没用开源;但不影响我们理解;
  • 操作例如:
    1010 ^ 1111 = 0101 (encode)
    0101 ^ 1111 = 1010 (decode)

1.3 对象类型

objc4-750之前:


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
}
  • classSlotForBasicTagIndex() 函数的主要功能就是根据指定索引 tag 从数组objc_tag_classes中获取类指针,而下标的计算方法发是根据外部传递的索引tag。比如字符串 tag = 2。当然这并不是简单的从数组中获取某条数据。

进一步了解这部分的操作,可参考内存管理之Tagged pointer

1.4 对象的值

static inline uintptr_t
_objc_getTaggedPointerValue(const void * _Nullable ptr) 
{
    // ASSERT(_objc_isTaggedPointer(ptr));
    uintptr_t value = _objc_decodeTaggedPointer_noPermute(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 uintptr_t
_objc_decodeTaggedPointer_noPermute(const void * _Nullable ptr)
{
    uintptr_t value = (uintptr_t)ptr;
#if OBJC_SPLIT_TAGGED_POINTERS
    if ((value & _OBJC_TAG_NO_OBFUSCATION_MASK) == _OBJC_TAG_NO_OBFUSCATION_MASK)
        return value;
#endif
    return value ^ objc_debug_taggedpointer_obfuscator;
}

测试:

NSString *str1 = [NSString stringWithFormat:@"1"];
NSString *str11 = [NSString stringWithFormat:@"11"];

uintptr_t value1 = _objc_getTaggedPointerValue((__bridge void *)str1);
uintptr_t value2 = _objc_getTaggedPointerValue((__bridge void *)str2);

输出:

TaggedPointer[89535:3033433] 311
TaggedPointer[89535:3033433] 31312

即 "1" = 0x31 在ASCII码表中31表示的就是字符1;
最后一位:“1”,"2" ,代表是字符串长度;

总结

TaggedPoint对象是一个特殊的对象,不会涉及到引用计数retainrelease等内存操作。对象的值就存在8位指针中,不过值通过了一份加密。


参考
内存管理之Tagged pointer

相关文章

网友评论

      本文标题:OC底层探索27-TaggedPoint

      本文链接:https://www.haomeiwen.com/subject/isvdpltx.html