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

1、 TaggedPointer简介

- 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对象是一个特殊的对象,不会涉及到引用计数retain
、release
等内存操作。对象的值就存在8位指针中,不过值通过了一份加密。
网友评论