TaggedPointer

64位系统才会有TaggedPointer功能,本意是为了减小内存浪费,提升性能。
TaggedPointer的相关参数的初始化是在libobjc库的_read_images中进行的。

        if (DisableTaggedPointers) {
            disableTaggedPointers();
        }
        
        initializeTaggedPointerObfuscator();
initializeTaggedPointerObfuscator(void)
{    
    if (sdkIsOlderThan(10_14, 12_0, 12_0, 5_0, 3_0) ||
        // Set the obfuscator to zero for apps linked against older SDKs,
        // in case they're relying on the tagged pointer representation.
        DisableTaggedPointerObfuscation) {
        objc_debug_taggedpointer_obfuscator = 0;
    } else {
        // Pull random data into the variable, then shift away all non-payload bits.
        arc4random_buf(&objc_debug_taggedpointer_obfuscator,
                       sizeof(objc_debug_taggedpointer_obfuscator));
        objc_debug_taggedpointer_obfuscator &= ~_OBJC_TAG_MASK;
    }
}

初始化混淆参数,系统版本小于 iOS 12,或启用了禁止混淆功能,则不进行混淆。混淆就是随机生成一个数值,用来对TaggedPointer的value进行混淆,保证安全。

objc-env.h中的定义

OPTION( DisableTaggedPointers,    OBJC_DISABLE_TAGGED_POINTERS,    "disable tagged pointer optimization of NSNumber et al.") 
OPTION( DisableTaggedPointerObfuscation, OBJC_DISABLE_TAG_OBFUSCATION,    "disable obfuscation of tagged pointers")

OBJC_DISABLE_TAGGED_POINTERS,是否禁用TaggedPointer。
OBJC_DISABLE_TAG_OBFUSCATION,是否禁止混淆。
当禁用了TaggedPointer,disableTaggedPointers()函数,将TaggedPointer相关的参数都置为0。
注意如果禁止TaggedPointer会在macOS平台的应用上崩溃,原因在libobjc框架里面的注册函数中会判断如果禁止则崩溃。而iOS平台则不会崩溃,因为在调用_objc_registerTaggedPointerClass之前先判断objc_debug_taggedpointer_mask的值,表示是否启用了TaggedPointer。
注:12.1的系统禁用TaggedPointer会崩溃,原因在于libxpc.dylib中_libxpc_initializer直接调用了_objc_registerTaggedPointerClass。

TaggedPointer 类的索引

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
};

这个索引是用来注册TaggedPointer类使用的,比如NSNumber的注册是在CoreFoundation中__CFInitialize中调用CFNumberGetTypeID来完成的。_read_images()先于__CFInitialize()执行

_objc_registerTaggedPointerClass(objc_tag_index_t tag, Class _Nonnull cls)
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
}

在禁止value混淆时,查看下地址

(__NSCFNumber *) $0 = 0xb000000000000012 (int)1

根据上面的代码流程,当传入的tag为3时,obfuscatedTag为3,而SUPPORT_MSB_TAGGED_POINTERS为0,最后[0x8 | obfuscatedTag]的运算结果是11,16进制的B。也就是objc_tag_classes[11]是NSNumber。
看下getIsa函数

inline Class 
objc_object::getIsa() 
{
    if (!isTaggedPointer()) return ISA();

    uintptr_t ptr = (uintptr_t)this;
    if (isExtTaggedPointer()) {
        uintptr_t slot = 
            (ptr >> _OBJC_TAG_EXT_SLOT_SHIFT) & _OBJC_TAG_EXT_SLOT_MASK;
        return objc_tag_ext_classes[slot];
    } else {
        uintptr_t slot = 
            (ptr >> _OBJC_TAG_SLOT_SHIFT) & _OBJC_TAG_SLOT_MASK;
        return objc_tag_classes[slot];
    }
}

最终会走到最下面的else,地址经过运算,>>60后与0xF与操作,也就是最高的四位,即b。和上面的class索引对应。

若在macOS系统上,
0x0000000000000127,第一个函数中的运算结果是(obfuscatedTag << 1) | 1,结果是7,
而0x0000000000000127的低四位就是7。

TaggedPointer的判断

#if TARGET_OS_OSX && __x86_64__
#   define __OBJC_TAG_MASK__ 1
#else
#   define __OBJC_TAG_MASK__ (1ULL<<63)
#endif

static inline bool
__objc_isTaggedPointer(const void *ptr)
{
    return ((intptr_t)ptr & __OBJC_TAG_MASK__) == __OBJC_TAG_MASK__;
}

即在macOS平台判断最低位是否为1,其他平台判断最高位是否为1

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值