源码编辑器下载r对象” alt=”[iOS开发]Tagged Pointer对字符型变量象” src=”https://www.6hu.cc/files/2022/06/260dd503b20e99f07a96aecb0f2f0fc6.p变量之间的关系ng”>
[iOS开发]Tagged Pointer对象源码1688“https://www.6hu.cc/files/2022/06/2f56a2c变量类型有哪些59e6bd1c7bee71b8ab4cddd60.png”> 可以看出,解析出来的tag就对应着它们的类型,看一下解析tag的方法:

static inline objc_tag_index_t
_objc_getTaggedPointerTag(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;
    uintptr_t extTag =   (value >> _OBJC_TAG_EXT_INDEX_SHIFT) & _OBJC_TAG_EXT_INDEX_MASK;
    if (basicTag == _OBJC_TAG_INDEX_MASK) {
        return (objc_tag_index_t)(extTag + OBJC_TAG_First52BitPayload);
    } else {
        return (objc_tag_index_t)basicTag;
    }
}

解析出来的pointerValue就代表着他们的值:NSNumber类后一位前的十六进制代表它们的真实值;NSS字符间距tring最后一位代表字符串长度,后面每两位代表多线程ASCII码值。 当8字节可以承载用于表示的数值时,系统就会以 Tagged Pointer 的方式生成指针,如果8字节承载不了时,则又用以前的方式来生成普通多线程是什么意思的指针。

特点

我们也可以看到苹果对于Tagged Pointer 特点的介绍:

  1. Tagg字符串逆序输出ed Pointer 专门用来存储小的对象,例如 NSNumberNS字符间距Date
  2. Tagged Pointer 指针的值不再是地址了,而是真正的值。所以,实际上它不再是一个对象

了,它只是一个披着对象“皮”的普通变量而己。所以,它的内存并不存储在堆中,也 不需要 mallocfree。 3. 在内存读取上有着以前3倍的效率,创建时比以前快 106倍

因此,苹果引入 Tagged Pointer,字符间距在哪里设置不但减少了 64位机器下程序的内存占用,还提高了运行效率,完美地解决了小内存对象在存储和访问效率上的问题。

注意事项

isa指针

Tagged Pointer 的引入也带来了问题字符间距在哪里设置,即 Tagged Pointer 并不是真正的对象,而是一个伪对象,所多线程的应用场景以你如果完全把它当成对象来使用,可能会让它“露马脚”。在上一章中我们写道,所有对象都有isa 指针,而 Tagged Pointer 其实是没有的,多线程是什么因为它不是真正的对象。

64位下的isa指针优化

对于 64位设备,苹果除了引人 Tagg效率符号ed Pointer 来优化小的对象外,对于普通的对象,其多线程的实现方式isa指针也进行了优化和调整。在32 位环境下,对象的引用计数都保存在一个外部的表中,每一个变量之间的关系对象的 Retain 操作,实际上包括如下 5个步骤:

  1. 获得全局的记录引用计数的hash 表。
  2. 为了线程安全,给该hash 表加锁。
  3. 查找到目标对效率公式象的引用计数值。
  4. 将该引用计数值加1,写回hash 表。
  5. 给该hash 表解锁。

从上面的步骤我们可以看出,为了保证源码交易平台线程安全,对引用计数的增减操作都要先锁定这个表,这从性能上看变量类型有哪些是非常差的。 而在 64 位环境下,isa 指针也是64 位,实际作为指针部分只用到的其中33位,剩余的 31位苹果使用了类似 Tagged Poin多线程并发中线程的状态ter 的概念,其中 19 位将保存对象的引1用计数,这样对引用计数的操作只需要修改源码这个指针即可。只有当引用计数超出19位,才会将引用计数保存到外部表,而这种情况是很少的,所以这样引用计数的更改效率会更高。 与前面的5个步骤对应,在64位字符间距怎么加宽环境下,新的 Ret多线程是什么ain 操作包括如下 5个步骤:

  1. 检查 isa 指针上面的标记位,看引1用计数是否保存在 isa变量中,如果不是,则使用以前

的步骤,否则执行第2步 2. 检查当前对象是源码编辑器下载否正在释放,如果是,则不做任何事情。 3. 增加该对象的变量类型有哪些引用计数,但是并不马上写回到isa 变量中。 4. 检杳增加后的引用计数的值是否能够被 19 位表示,如果不是,则切换成以前的办法,否 则执行第5步。 5. 进行一个原子的写操作,源码之家isa 的值写回。

虽然步骤都是 5步,但是由于没有了全局的加锁操作,所以引用源码编程器计数的更改更快了。

[iOS开发]Tagged Pointer对象多线程应用场景例子rc=”https://www.6hu.cc/files/2022/06/931ed132d53d87dc7bc3728e3358b912.png”> 这里可以看出 需要经过 decod源码编程器ed_obj 加上一些位运算操作得到,说明是一个加密解密的变量类型有哪些过程,下面我们来看一下 decode

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;
}
static inline uintptr_t
_objc_decodeTaggedPointer(const void * _Nullable ptr)
{
    uintptr_t value = _objc_decodeTaggedPointer_noPermute(ptr);
#if OBJC_SPLIT_TAGGED_POINTERS
    uintptr_t basicTag = (value >> _OBJC_TAG_INDEX_SHIFT) & _OBJC_TAG_INDEX_MASK;
    value &= ~(_OBJC_TAG_INDEX_MASK << _OBJC_TAG_INDEX_SHIFT);
    value |= _objc_obfuscatedTagToBasicTag(basicTag) << _OBJC_TAG_INDEX_SHIFT;
#endif
    return value;
}

它是多线程应用场景例子由两个过程融合的。_objc_decodeTaggedPointer_noPermu字符是什么te()这个函数结束最后返回的 value 等于 value ^ objc_debug_taggedpoin变量与函数ter_obfusc字符串是什么意思ator,而 objc_debug_taggedpointer_obf源码编程器uscator 代表混淆,在initializeTaggedPointerObfuscator多线程应用场景例子()里可以看到它的初始变量化:

if (!DisableTaggedPointerObfuscation && dyld_program_sdk_at_least(dyld_fall_2018_os_versions)) {
    // 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;
#if OBJC_SPLIT_TAGGED_POINTERS
    // The obfuscator doesn't apply to any of the extended tag mask or the no-obfuscation bit.
    objc_debug_taggedpointer_obfuscator &= ~(_OBJC_TAG_EXT_MASK | _OBJC_TAG_NO_OBFUSCATION_MASK);
    // Shuffle the first seven entries of the tag permutator.
    int max = 7;
    for (int i = max - 1; i >= 0; i--) {
        int target = arc4random_uniform(i + 1);
        swap(objc_debug_tag60_permutations[i],
             objc_debug_tag60_permutations[target]);
    }
#endif
}

是赋随字符是什么机值。 接下多线程的实现方式来我们去源码里看看tag的值和代表的类型:

{
    // 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_NSColor           = 16,
    OBJC_TAG_UIColor           = 17,
    OBJC_TAG_CGColor           = 18,
    OBJC_TAG_NSIndexSet        = 19,
    OBJC_TAG_NSMethodSignature = 20,
    OBJC_TAG_UTTypeRecord      = 21,
    // When using the split tagged pointer representation
    // (OBJC_SPLIT_TAGGED_POINTERS), this is the first tag where
    // the tag and payload are unobfuscated. All tags from here to
    // OBJC_TAG_Last52BitPayload are unobfuscated. The shared cache
    // builder is able to construct these as long as the low bit is
    // not set (i.e. even-numbered tags).
    OBJC_TAG_FirstUnobfuscatedSplitTag = 136, // 128 + 8, first ext tag with high bit set
    OBJC_TAG_Constant_CFString = 136,
    OBJC_TAG_First60BitPayload = 0,
    OBJC_TAG_Last60BitPayload  = 6,
    OBJC_TAG_First52BitPayload = 8,
    OBJC_TAG_Last52BitPayload  = 263,
    OBJC_TAG_RESERVED_264      = 264
};

接下效率集来,我们使用系统的方法解析一下地址,按照,看一下它们代表的真实含义吧:源码

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        NSNumber *number0 = [NSNumber numberWithInt:0];
        NSNumber *number1 = [NSNumber numberWithInt:1];
        NSNumber *number2 = [NSNumber numberWithInt:2];
        NSNumber *number3 = [NSNumber numberWithInt:3];
        NSNumber *number4 = [NSNumber numberWithInt:4];
        NSNumber *number50 = [NSNumber numberWithInt:50];
#pragma mark number1
        NSLog(@"number1 pointer is %p---PointerValue:==0x%lx", number1, _objc_getTaggedPointerValue((__bridge void *)(number1)));
        NSLog(@"    decode:==0x%lx tag:%hu", _objc_decodeTaggedPointer((__bridge void *)(number1)), _objc_getTaggedPointerTag((__bridge void *)(number1)));
        NSLog(@"    %@", getBinaryByHex([NSString stringWithFormat:@"%lx", _objc_decodeTaggedPointer((__bridge void *)(number1))]));
#pragma mark number2
        NSLog(@"number2 pointer is %p---PointerValue:==0x%lx", number2, _objc_getTaggedPointerValue((__bridge void *)(number2)));
        NSLog(@"    decode:==0x%lx tag:%hu", _objc_decodeTaggedPointer((__bridge void *)(number2)), _objc_getTaggedPointerTag((__bridge void *)(number2)));
        NSLog(@"    %@", getBinaryByHex([NSString stringWithFormat:@"%lx", _objc_decodeTaggedPointer((__bridge void *)(number2))]));
#pragma mark number3
        NSLog(@"number3 pointer is %p---PointerValue:==0x%lx", number3, _objc_getTaggedPointerValue((__bridge void *)(number3)));
        NSLog(@"    decode:==0x%lx tag:%hu", _objc_decodeTaggedPointer((__bridge void *)(number3)), _objc_getTaggedPointerTag((__bridge void *)(number3)));
        NSLog(@"    %@", getBinaryByHex([NSString stringWithFormat:@"%lx", _objc_decodeTaggedPointer((__bridge void *)(number3))]));
#pragma mark number50
        NSLog(@"number50 pointer is %p---PointerValue:==0x%lx", number50, _objc_getTaggedPointerValue((__bridge void *)(number50)));
        NSLog(@"    decode:==0x%lx tag:%hu", _objc_decodeTaggedPointer((__bridge void *)(number50)), _objc_getTaggedPointerTag((__bridge void *)(number50)));
        NSLog(@"    %@", getBinaryByHex([NSString stringWithFormat:@"%lx", _objc_decodeTaggedPointer((__bridge void *)(number50))]));
        NSString *str1 = [NSString stringWithFormat:@"a"];
        NSString *str2 = [NSString stringWithFormat:@"ab"];
        NSString *str3 = [NSString stringWithFormat:@"abc"];
        NSString *str4 = [NSString stringWithFormat:@"abcf"];
#pragma mark str1
        NSLog(@"str1 pointer is %p---PointerValue:==0x%lx", str1, _objc_getTaggedPointerValue((__bridge void *)(str1)));
        NSLog(@"    %@", binForObjectPointer(str1));
        NSLog(@"    decode:==0x%lx tag:%hu", _objc_decodeTaggedPointer((__bridge void *)(str1)), _objc_getTaggedPointerTag((__bridge void *)(str1)));
        NSLog(@"    %@", getBinaryByHex([NSString stringWithFormat:@"%lx", _objc_decodeTaggedPointer((__bridge void *)(str1))]));
        NSLog(@"    %@", getBinaryByHex([NSString stringWithFormat:@"%p", str1]));
#pragma mark str2
        NSLog(@"str2 pointer is %p---PointerValue:==0x%lx", str2, _objc_getTaggedPointerValue((__bridge void *)(str2)));
        NSLog(@"    decode:==0x%lx tag:%hu", _objc_decodeTaggedPointer((__bridge void *)(str2)), _objc_getTaggedPointerTag((__bridge void *)(str2)));
        NSLog(@"    %@", getBinaryByHex([NSString stringWithFormat:@"%lx", _objc_decodeTaggedPointer((__bridge void *)(str2))]));
        NSLog(@"    %@", getBinaryByHex([NSString stringWithFormat:@"%p", str2]));
#pragma mark str3
        NSLog(@"str3 pointer is %p---PointerValue:==0x%lx", str3, _objc_getTaggedPointerValue((__bridge void *)(str3)));
        NSLog(@"    decode:==0x%lx tag:%hu", _objc_decodeTaggedPointer((__bridge void *)(str3)), _objc_getTaggedPointerTag((__bridge void *)(str3)));
        NSLog(@"    %@", getBinaryByHex([NSString stringWithFormat:@"%lx", _objc_decodeTaggedPointer((__bridge void *)(str3))]));
        NSLog(@"    %@", getBinaryByHex([NSString stringWithFormat:@"%p", str3]));
#pragma mark str4
        NSLog(@"str4 pointer is %p---PointerValue:==0x%lx", str4, _objc_getTaggedPointerValue((__bridge void *)(str4)));
        NSLog(@"    decode:==0x%lx tag:%hu", _objc_decodeTaggedPointer((__bridge void *)(str4)), _objc_getTaggedPointerTag((__bridge void *)(str4)));
        NSLog(@"    %@", getBinaryByHex([NSString stringWithFormat:@"%lx", _objc_decodeTaggedPointer((__bridge void *)(str4))]));
        NSLog(@"    %@", getBinaryByHex([NSString stringWithFormat:@"%p", str4]));
#pragma mark indexPath
        NSIndexPath *indexPath = [[NSIndexPath alloc] initWithIndex:4];
        NSLog(@"indexPath pointer is %p---PointerValue:==0x%lx", indexPath, _objc_getTaggedPointerValue((__bridge void *)(indexPath)));
        NSLog(@"    decode:==0x%lx tag:%hu", _objc_decodeTaggedPointer((__bridge void *)(indexPath)), _objc_getTaggedPointerTag((__bridge void *)(indexPath)));
        NSLog(@"    %@", getBinaryByHex([NSString stringWithFormat:@"%lx", _objc_decodeTaggedPointer((__bridge void *)(indexPath))]));
    }
    return 0;
}

输出:

源码编辑器下载r对象” alt=”[iOS开发]Tagged Pointer对字符型变量象” src=”https://www.6hu.cc/files/2022/06/260dd503b20e99f07a96aecb0f2f0fc6.p变量之间的关系ng”>
[iOS开发]Tagged Pointer对象源码1688“https://www.6hu.cc/files/2022/06/2f56a2c变量类型有哪些59e6bd1c7bee71b8ab4cddd60.png”> 可以看出,解析出来的tag就对应着它们的类型,看一下解析tag的方法:

static inline objc_tag_index_t
_objc_getTaggedPointerTag(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;
    uintptr_t extTag =   (value >> _OBJC_TAG_EXT_INDEX_SHIFT) & _OBJC_TAG_EXT_INDEX_MASK;
    if (basicTag == _OBJC_TAG_INDEX_MASK) {
        return (objc_tag_index_t)(extTag + OBJC_TAG_First52BitPayload);
    } else {
        return (objc_tag_index_t)basicTag;
    }
}

解析出来的pointerValue就代表着他们的值:NSNumber类后一位前的十六进制代表它们的真实值;NSS字符间距tring最后一位代表字符串长度,后面每两位代表多线程ASCII码值。 当8字节可以承载用于表示的数值时,系统就会以 Tagged Pointer 的方式生成指针,如果8字节承载不了时,则又用以前的方式来生成普通多线程是什么意思的指针。

特点

我们也可以看到苹果对于Tagged Pointer 特点的介绍:

  1. Tagg字符串逆序输出ed Pointer 专门用来存储小的对象,例如 NSNumberNS字符间距Date
  2. Tagged Pointer 指针的值不再是地址了,而是真正的值。所以,实际上它不再是一个对象

了,它只是一个披着对象“皮”的普通变量而己。所以,它的内存并不存储在堆中,也 不需要 mallocfree。 3. 在内存读取上有着以前3倍的效率,创建时比以前快 106倍

因此,苹果引入 Tagged Pointer,字符间距在哪里设置不但减少了 64位机器下程序的内存占用,还提高了运行效率,完美地解决了小内存对象在存储和访问效率上的问题。

注意事项

isa指针

Tagged Pointer 的引入也带来了问题字符间距在哪里设置,即 Tagged Pointer 并不是真正的对象,而是一个伪对象,所多线程的应用场景以你如果完全把它当成对象来使用,可能会让它“露马脚”。在上一章中我们写道,所有对象都有isa 指针,而 Tagged Pointer 其实是没有的,多线程是什么因为它不是真正的对象。

64位下的isa指针优化

对于 64位设备,苹果除了引人 Tagg效率符号ed Pointer 来优化小的对象外,对于普通的对象,其多线程的实现方式isa指针也进行了优化和调整。在32 位环境下,对象的引用计数都保存在一个外部的表中,每一个变量之间的关系对象的 Retain 操作,实际上包括如下 5个步骤:

  1. 获得全局的记录引用计数的hash 表。
  2. 为了线程安全,给该hash 表加锁。
  3. 查找到目标对效率公式象的引用计数值。
  4. 将该引用计数值加1,写回hash 表。
  5. 给该hash 表解锁。

从上面的步骤我们可以看出,为了保证源码交易平台线程安全,对引用计数的增减操作都要先锁定这个表,这从性能上看变量类型有哪些是非常差的。 而在 64 位环境下,isa 指针也是64 位,实际作为指针部分只用到的其中33位,剩余的 31位苹果使用了类似 Tagged Poin多线程并发中线程的状态ter 的概念,其中 19 位将保存对象的引1用计数,这样对引用计数的操作只需要修改源码这个指针即可。只有当引用计数超出19位,才会将引用计数保存到外部表,而这种情况是很少的,所以这样引用计数的更改效率会更高。 与前面的5个步骤对应,在64位字符间距怎么加宽环境下,新的 Ret多线程是什么ain 操作包括如下 5个步骤:

  1. 检查 isa 指针上面的标记位,看引1用计数是否保存在 isa变量中,如果不是,则使用以前

的步骤,否则执行第2步 2. 检查当前对象是源码编辑器下载否正在释放,如果是,则不做任何事情。 3. 增加该对象的变量类型有哪些引用计数,但是并不马上写回到isa 变量中。 4. 检杳增加后的引用计数的值是否能够被 19 位表示,如果不是,则切换成以前的办法,否 则执行第5步。 5. 进行一个原子的写操作,源码之家isa 的值写回。

虽然步骤都是 5步,但是由于没有了全局的加锁操作,所以引用源码编程器计数的更改更快了。

[iOS开发]Tagged Pointer对象 原因分析: 因为setter方法中,对strong修饰的属性会有一个retainrelease的操作。在并发多线程的赋值操作中,都是对_name指针进行的操作,可能在_name刚刚被release后进行赋值操作,这个时候_name指向的内存地址是已经被释放了,所以造成了坏内存访问崩溃。

- (void)setName:(NSString *)name {
    [name retain];
    [_name relase];
    _name = name;
}

解决办法:

  1. 将并发执效率计算公式行的任务改为串行执行
dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_SERIAL);
  1. 将异步执行改为同步执行
dispatch_sync(queue, ^{
 	self.name = [NSString stringWithFormat:@"abcdefghijklmn"];
});
  1. 将属性改为atomic属性原子性
  2. 加锁
myClass *test = [[myClass alloc] init];
NSLock *lock = [[NSLock alloc] init];
dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_CONCURRENT);
for (int i = 0; i < 1000; i ++) {
	dispatch_async(queue, ^{
		[lock lock];
		test.name = [NSString stringWithFormat:@"abcdefghijklmn"];
		[lock unlock];
	});
}
NSLog(@"end");

再看以下代码执行结果是什么,不崩溃了。因为没有用到引用计数的内存管理方法,使用源码编辑器下载的是Tagged Pointer

TaggedPointer 结构

苹果为了安全对源码编辑器下载其做了编码,runtime内变量类型有哪些部实现了编码、解码方法,我们变量与函数看一下: 编码:

static inline void * _Nonnull
_objc_encodeTaggedPointer(uintptr_t ptr)
{
    uintptr_t value = (objc_debug_taggedpointer_obfuscator ^ ptr);
#if OBJC_SPLIT_TAGGED_POINTERS
    if ((value & _OBJC_TAG_NO_OBFUSCATION_MASK) == _OBJC_TAG_NO_OBFUSCATION_MASK)
        return (void *)ptr;
    uintptr_t basicTag = (value >> _OBJC_TAG_INDEX_SHIFT) & _OBJC_TAG_INDEX_MASK;
    uintptr_t permutedTag = _objc_basicTagToObfuscatedTag(basicTag);
    value &= ~(_OBJC_TAG_INDEX_MASK << _OBJC_TAG_INDEX_SHIFT);
    value |= permutedTag << _OBJC_TAG_INDEX_SHIFT;
#endif
    return (void *)value;
}

里面进行了一系列的位运算。 我们可以试着打印地址:

NSNumber *number1 = [NSNumber numberWithInt:1];
NSLog(@"number1 pointer is %p", number1);

输出:

number1 pointer is 0xbb027d74df7d32ea

可见,这个地址是被编码过的效率是什么意思。通过资料的查询,我们可以对其结构有个了解:

  • Tagged Po效率集inter 标记:x86最后一位是标记位,arm64最高位是标记源码编辑器下载位。1表示是Tagged Pointer对象,0表示是普通对象。
  • Tag:对象类型标记。x86为1~3位,arm64为0~2。7表示有扩展信息。
  • Exten变量的定义ded:x86为4~11位,arm64为54~62变量名。用来扩展更多类型。
  • payload:有效负载。存储真正的数据(除了标记位、tag以及extended),不过为了安全苹果做字符串逆序输出了编码。

payload 代表有效负载,源码中有这样的注释:

[iOS开发]Tagged Pointer对象多线程应用场景例子rc=”https://www.6hu.cc/files/2022/06/931ed132d53d87dc7bc3728e3358b912.png”> 这里可以看出 需要经过 decod源码编程器ed_obj 加上一些位运算操作得到,说明是一个加密解密的变量类型有哪些过程,下面我们来看一下 decode

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;
}
static inline uintptr_t
_objc_decodeTaggedPointer(const void * _Nullable ptr)
{
    uintptr_t value = _objc_decodeTaggedPointer_noPermute(ptr);
#if OBJC_SPLIT_TAGGED_POINTERS
    uintptr_t basicTag = (value >> _OBJC_TAG_INDEX_SHIFT) & _OBJC_TAG_INDEX_MASK;
    value &= ~(_OBJC_TAG_INDEX_MASK << _OBJC_TAG_INDEX_SHIFT);
    value |= _objc_obfuscatedTagToBasicTag(basicTag) << _OBJC_TAG_INDEX_SHIFT;
#endif
    return value;
}

它是多线程应用场景例子由两个过程融合的。_objc_decodeTaggedPointer_noPermu字符是什么te()这个函数结束最后返回的 value 等于 value ^ objc_debug_taggedpoin变量与函数ter_obfusc字符串是什么意思ator,而 objc_debug_taggedpointer_obf源码编程器uscator 代表混淆,在initializeTaggedPointerObfuscator多线程应用场景例子()里可以看到它的初始变量化:

if (!DisableTaggedPointerObfuscation && dyld_program_sdk_at_least(dyld_fall_2018_os_versions)) {
    // 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;
#if OBJC_SPLIT_TAGGED_POINTERS
    // The obfuscator doesn't apply to any of the extended tag mask or the no-obfuscation bit.
    objc_debug_taggedpointer_obfuscator &= ~(_OBJC_TAG_EXT_MASK | _OBJC_TAG_NO_OBFUSCATION_MASK);
    // Shuffle the first seven entries of the tag permutator.
    int max = 7;
    for (int i = max - 1; i >= 0; i--) {
        int target = arc4random_uniform(i + 1);
        swap(objc_debug_tag60_permutations[i],
             objc_debug_tag60_permutations[target]);
    }
#endif
}

是赋随字符是什么机值。 接下多线程的实现方式来我们去源码里看看tag的值和代表的类型:

{
    // 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_NSColor           = 16,
    OBJC_TAG_UIColor           = 17,
    OBJC_TAG_CGColor           = 18,
    OBJC_TAG_NSIndexSet        = 19,
    OBJC_TAG_NSMethodSignature = 20,
    OBJC_TAG_UTTypeRecord      = 21,
    // When using the split tagged pointer representation
    // (OBJC_SPLIT_TAGGED_POINTERS), this is the first tag where
    // the tag and payload are unobfuscated. All tags from here to
    // OBJC_TAG_Last52BitPayload are unobfuscated. The shared cache
    // builder is able to construct these as long as the low bit is
    // not set (i.e. even-numbered tags).
    OBJC_TAG_FirstUnobfuscatedSplitTag = 136, // 128 + 8, first ext tag with high bit set
    OBJC_TAG_Constant_CFString = 136,
    OBJC_TAG_First60BitPayload = 0,
    OBJC_TAG_Last60BitPayload  = 6,
    OBJC_TAG_First52BitPayload = 8,
    OBJC_TAG_Last52BitPayload  = 263,
    OBJC_TAG_RESERVED_264      = 264
};

接下效率集来,我们使用系统的方法解析一下地址,按照,看一下它们代表的真实含义吧:源码

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        NSNumber *number0 = [NSNumber numberWithInt:0];
        NSNumber *number1 = [NSNumber numberWithInt:1];
        NSNumber *number2 = [NSNumber numberWithInt:2];
        NSNumber *number3 = [NSNumber numberWithInt:3];
        NSNumber *number4 = [NSNumber numberWithInt:4];
        NSNumber *number50 = [NSNumber numberWithInt:50];
#pragma mark number1
        NSLog(@"number1 pointer is %p---PointerValue:==0x%lx", number1, _objc_getTaggedPointerValue((__bridge void *)(number1)));
        NSLog(@"    decode:==0x%lx tag:%hu", _objc_decodeTaggedPointer((__bridge void *)(number1)), _objc_getTaggedPointerTag((__bridge void *)(number1)));
        NSLog(@"    %@", getBinaryByHex([NSString stringWithFormat:@"%lx", _objc_decodeTaggedPointer((__bridge void *)(number1))]));
#pragma mark number2
        NSLog(@"number2 pointer is %p---PointerValue:==0x%lx", number2, _objc_getTaggedPointerValue((__bridge void *)(number2)));
        NSLog(@"    decode:==0x%lx tag:%hu", _objc_decodeTaggedPointer((__bridge void *)(number2)), _objc_getTaggedPointerTag((__bridge void *)(number2)));
        NSLog(@"    %@", getBinaryByHex([NSString stringWithFormat:@"%lx", _objc_decodeTaggedPointer((__bridge void *)(number2))]));
#pragma mark number3
        NSLog(@"number3 pointer is %p---PointerValue:==0x%lx", number3, _objc_getTaggedPointerValue((__bridge void *)(number3)));
        NSLog(@"    decode:==0x%lx tag:%hu", _objc_decodeTaggedPointer((__bridge void *)(number3)), _objc_getTaggedPointerTag((__bridge void *)(number3)));
        NSLog(@"    %@", getBinaryByHex([NSString stringWithFormat:@"%lx", _objc_decodeTaggedPointer((__bridge void *)(number3))]));
#pragma mark number50
        NSLog(@"number50 pointer is %p---PointerValue:==0x%lx", number50, _objc_getTaggedPointerValue((__bridge void *)(number50)));
        NSLog(@"    decode:==0x%lx tag:%hu", _objc_decodeTaggedPointer((__bridge void *)(number50)), _objc_getTaggedPointerTag((__bridge void *)(number50)));
        NSLog(@"    %@", getBinaryByHex([NSString stringWithFormat:@"%lx", _objc_decodeTaggedPointer((__bridge void *)(number50))]));
        NSString *str1 = [NSString stringWithFormat:@"a"];
        NSString *str2 = [NSString stringWithFormat:@"ab"];
        NSString *str3 = [NSString stringWithFormat:@"abc"];
        NSString *str4 = [NSString stringWithFormat:@"abcf"];
#pragma mark str1
        NSLog(@"str1 pointer is %p---PointerValue:==0x%lx", str1, _objc_getTaggedPointerValue((__bridge void *)(str1)));
        NSLog(@"    %@", binForObjectPointer(str1));
        NSLog(@"    decode:==0x%lx tag:%hu", _objc_decodeTaggedPointer((__bridge void *)(str1)), _objc_getTaggedPointerTag((__bridge void *)(str1)));
        NSLog(@"    %@", getBinaryByHex([NSString stringWithFormat:@"%lx", _objc_decodeTaggedPointer((__bridge void *)(str1))]));
        NSLog(@"    %@", getBinaryByHex([NSString stringWithFormat:@"%p", str1]));
#pragma mark str2
        NSLog(@"str2 pointer is %p---PointerValue:==0x%lx", str2, _objc_getTaggedPointerValue((__bridge void *)(str2)));
        NSLog(@"    decode:==0x%lx tag:%hu", _objc_decodeTaggedPointer((__bridge void *)(str2)), _objc_getTaggedPointerTag((__bridge void *)(str2)));
        NSLog(@"    %@", getBinaryByHex([NSString stringWithFormat:@"%lx", _objc_decodeTaggedPointer((__bridge void *)(str2))]));
        NSLog(@"    %@", getBinaryByHex([NSString stringWithFormat:@"%p", str2]));
#pragma mark str3
        NSLog(@"str3 pointer is %p---PointerValue:==0x%lx", str3, _objc_getTaggedPointerValue((__bridge void *)(str3)));
        NSLog(@"    decode:==0x%lx tag:%hu", _objc_decodeTaggedPointer((__bridge void *)(str3)), _objc_getTaggedPointerTag((__bridge void *)(str3)));
        NSLog(@"    %@", getBinaryByHex([NSString stringWithFormat:@"%lx", _objc_decodeTaggedPointer((__bridge void *)(str3))]));
        NSLog(@"    %@", getBinaryByHex([NSString stringWithFormat:@"%p", str3]));
#pragma mark str4
        NSLog(@"str4 pointer is %p---PointerValue:==0x%lx", str4, _objc_getTaggedPointerValue((__bridge void *)(str4)));
        NSLog(@"    decode:==0x%lx tag:%hu", _objc_decodeTaggedPointer((__bridge void *)(str4)), _objc_getTaggedPointerTag((__bridge void *)(str4)));
        NSLog(@"    %@", getBinaryByHex([NSString stringWithFormat:@"%lx", _objc_decodeTaggedPointer((__bridge void *)(str4))]));
        NSLog(@"    %@", getBinaryByHex([NSString stringWithFormat:@"%p", str4]));
#pragma mark indexPath
        NSIndexPath *indexPath = [[NSIndexPath alloc] initWithIndex:4];
        NSLog(@"indexPath pointer is %p---PointerValue:==0x%lx", indexPath, _objc_getTaggedPointerValue((__bridge void *)(indexPath)));
        NSLog(@"    decode:==0x%lx tag:%hu", _objc_decodeTaggedPointer((__bridge void *)(indexPath)), _objc_getTaggedPointerTag((__bridge void *)(indexPath)));
        NSLog(@"    %@", getBinaryByHex([NSString stringWithFormat:@"%lx", _objc_decodeTaggedPointer((__bridge void *)(indexPath))]));
    }
    return 0;
}

输出:

源码编辑器下载r对象” alt=”[iOS开发]Tagged Pointer对字符型变量象” src=”https://www.6hu.cc/files/2022/06/260dd503b20e99f07a96aecb0f2f0fc6.p变量之间的关系ng”>
[iOS开发]Tagged Pointer对象源码1688“https://www.6hu.cc/files/2022/06/2f56a2c变量类型有哪些59e6bd1c7bee71b8ab4cddd60.png”> 可以看出,解析出来的tag就对应着它们的类型,看一下解析tag的方法:

static inline objc_tag_index_t
_objc_getTaggedPointerTag(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;
    uintptr_t extTag =   (value >> _OBJC_TAG_EXT_INDEX_SHIFT) & _OBJC_TAG_EXT_INDEX_MASK;
    if (basicTag == _OBJC_TAG_INDEX_MASK) {
        return (objc_tag_index_t)(extTag + OBJC_TAG_First52BitPayload);
    } else {
        return (objc_tag_index_t)basicTag;
    }
}

解析出来的pointerValue就代表着他们的值:NSNumber类后一位前的十六进制代表它们的真实值;NSS字符间距tring最后一位代表字符串长度,后面每两位代表多线程ASCII码值。 当8字节可以承载用于表示的数值时,系统就会以 Tagged Pointer 的方式生成指针,如果8字节承载不了时,则又用以前的方式来生成普通多线程是什么意思的指针。

特点

我们也可以看到苹果对于Tagged Pointer 特点的介绍:

  1. Tagg字符串逆序输出ed Pointer 专门用来存储小的对象,例如 NSNumberNS字符间距Date
  2. Tagged Pointer 指针的值不再是地址了,而是真正的值。所以,实际上它不再是一个对象

了,它只是一个披着对象“皮”的普通变量而己。所以,它的内存并不存储在堆中,也 不需要 mallocfree。 3. 在内存读取上有着以前3倍的效率,创建时比以前快 106倍

因此,苹果引入 Tagged Pointer,字符间距在哪里设置不但减少了 64位机器下程序的内存占用,还提高了运行效率,完美地解决了小内存对象在存储和访问效率上的问题。

注意事项

isa指针

Tagged Pointer 的引入也带来了问题字符间距在哪里设置,即 Tagged Pointer 并不是真正的对象,而是一个伪对象,所多线程的应用场景以你如果完全把它当成对象来使用,可能会让它“露马脚”。在上一章中我们写道,所有对象都有isa 指针,而 Tagged Pointer 其实是没有的,多线程是什么因为它不是真正的对象。

64位下的isa指针优化

对于 64位设备,苹果除了引人 Tagg效率符号ed Pointer 来优化小的对象外,对于普通的对象,其多线程的实现方式isa指针也进行了优化和调整。在32 位环境下,对象的引用计数都保存在一个外部的表中,每一个变量之间的关系对象的 Retain 操作,实际上包括如下 5个步骤:

  1. 获得全局的记录引用计数的hash 表。
  2. 为了线程安全,给该hash 表加锁。
  3. 查找到目标对效率公式象的引用计数值。
  4. 将该引用计数值加1,写回hash 表。
  5. 给该hash 表解锁。

从上面的步骤我们可以看出,为了保证源码交易平台线程安全,对引用计数的增减操作都要先锁定这个表,这从性能上看变量类型有哪些是非常差的。 而在 64 位环境下,isa 指针也是64 位,实际作为指针部分只用到的其中33位,剩余的 31位苹果使用了类似 Tagged Poin多线程并发中线程的状态ter 的概念,其中 19 位将保存对象的引1用计数,这样对引用计数的操作只需要修改源码这个指针即可。只有当引用计数超出19位,才会将引用计数保存到外部表,而这种情况是很少的,所以这样引用计数的更改效率会更高。 与前面的5个步骤对应,在64位字符间距怎么加宽环境下,新的 Ret多线程是什么ain 操作包括如下 5个步骤:

  1. 检查 isa 指针上面的标记位,看引1用计数是否保存在 isa变量中,如果不是,则使用以前

的步骤,否则执行第2步 2. 检查当前对象是源码编辑器下载否正在释放,如果是,则不做任何事情。 3. 增加该对象的变量类型有哪些引用计数,但是并不马上写回到isa 变量中。 4. 检杳增加后的引用计数的值是否能够被 19 位表示,如果不是,则切换成以前的办法,否 则执行第5步。 5. 进行一个原子的写操作,源码之家isa 的值写回。

虽然步骤都是 5步,但是由于没有了全局的加锁操作,所以引用源码编程器计数的更改更快了。

[iOS开发]Tagged Pointer对象多线程应用场景例子8345cf21f0.png”> 我们先看下下面这段代码:

dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_CONCURRENT);
for (int i = 0; i < 1000; i ++) {
	dispatch_async(queue, ^{
		self.name = [NSString stringWithFormat:@"abcdefghijklmn"];
	});
}
NSLog(@"end");

运行结果:崩溃(坏内存访问)

[iOS开发]Tagged Pointer对象 原因分析: 因为setter方法中,对strong修饰的属性会有一个retainrelease的操作。在并发多线程的赋值操作中,都是对_name指针进行的操作,可能在_name刚刚被release后进行赋值操作,这个时候_name指向的内存地址是已经被释放了,所以造成了坏内存访问崩溃。

- (void)setName:(NSString *)name {
    [name retain];
    [_name relase];
    _name = name;
}

解决办法:

  1. 将并发执效率计算公式行的任务改为串行执行
dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_SERIAL);
  1. 将异步执行改为同步执行
dispatch_sync(queue, ^{
 	self.name = [NSString stringWithFormat:@"abcdefghijklmn"];
});
  1. 将属性改为atomic属性原子性
  2. 加锁
myClass *test = [[myClass alloc] init];
NSLock *lock = [[NSLock alloc] init];
dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_CONCURRENT);
for (int i = 0; i < 1000; i ++) {
	dispatch_async(queue, ^{
		[lock lock];
		test.name = [NSString stringWithFormat:@"abcdefghijklmn"];
		[lock unlock];
	});
}
NSLog(@"end");

再看以下代码执行结果是什么,不崩溃了。因为没有用到引用计数的内存管理方法,使用源码编辑器下载的是Tagged Pointer

TaggedPointer 结构

苹果为了安全对源码编辑器下载其做了编码,runtime内变量类型有哪些部实现了编码、解码方法,我们变量与函数看一下: 编码:

static inline void * _Nonnull
_objc_encodeTaggedPointer(uintptr_t ptr)
{
    uintptr_t value = (objc_debug_taggedpointer_obfuscator ^ ptr);
#if OBJC_SPLIT_TAGGED_POINTERS
    if ((value & _OBJC_TAG_NO_OBFUSCATION_MASK) == _OBJC_TAG_NO_OBFUSCATION_MASK)
        return (void *)ptr;
    uintptr_t basicTag = (value >> _OBJC_TAG_INDEX_SHIFT) & _OBJC_TAG_INDEX_MASK;
    uintptr_t permutedTag = _objc_basicTagToObfuscatedTag(basicTag);
    value &= ~(_OBJC_TAG_INDEX_MASK << _OBJC_TAG_INDEX_SHIFT);
    value |= permutedTag << _OBJC_TAG_INDEX_SHIFT;
#endif
    return (void *)value;
}

里面进行了一系列的位运算。 我们可以试着打印地址:

NSNumber *number1 = [NSNumber numberWithInt:1];
NSLog(@"number1 pointer is %p", number1);

输出:

number1 pointer is 0xbb027d74df7d32ea

可见,这个地址是被编码过的效率是什么意思。通过资料的查询,我们可以对其结构有个了解:

  • Tagged Po效率集inter 标记:x86最后一位是标记位,arm64最高位是标记源码编辑器下载位。1表示是Tagged Pointer对象,0表示是普通对象。
  • Tag:对象类型标记。x86为1~3位,arm64为0~2。7表示有扩展信息。
  • Exten变量的定义ded:x86为4~11位,arm64为54~62变量名。用来扩展更多类型。
  • payload:有效负载。存储真正的数据(除了标记位、tag以及extended),不过为了安全苹果做字符串逆序输出了编码。

payload 代表有效负载,源码中有这样的注释:

[iOS开发]Tagged Pointer对象多线程应用场景例子rc=”https://www.6hu.cc/files/2022/06/931ed132d53d87dc7bc3728e3358b912.png”> 这里可以看出 需要经过 decod源码编程器ed_obj 加上一些位运算操作得到,说明是一个加密解密的变量类型有哪些过程,下面我们来看一下 decode

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;
}
static inline uintptr_t
_objc_decodeTaggedPointer(const void * _Nullable ptr)
{
    uintptr_t value = _objc_decodeTaggedPointer_noPermute(ptr);
#if OBJC_SPLIT_TAGGED_POINTERS
    uintptr_t basicTag = (value >> _OBJC_TAG_INDEX_SHIFT) & _OBJC_TAG_INDEX_MASK;
    value &= ~(_OBJC_TAG_INDEX_MASK << _OBJC_TAG_INDEX_SHIFT);
    value |= _objc_obfuscatedTagToBasicTag(basicTag) << _OBJC_TAG_INDEX_SHIFT;
#endif
    return value;
}

它是多线程应用场景例子由两个过程融合的。_objc_decodeTaggedPointer_noPermu字符是什么te()这个函数结束最后返回的 value 等于 value ^ objc_debug_taggedpoin变量与函数ter_obfusc字符串是什么意思ator,而 objc_debug_taggedpointer_obf源码编程器uscator 代表混淆,在initializeTaggedPointerObfuscator多线程应用场景例子()里可以看到它的初始变量化:

if (!DisableTaggedPointerObfuscation && dyld_program_sdk_at_least(dyld_fall_2018_os_versions)) {
    // 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;
#if OBJC_SPLIT_TAGGED_POINTERS
    // The obfuscator doesn't apply to any of the extended tag mask or the no-obfuscation bit.
    objc_debug_taggedpointer_obfuscator &= ~(_OBJC_TAG_EXT_MASK | _OBJC_TAG_NO_OBFUSCATION_MASK);
    // Shuffle the first seven entries of the tag permutator.
    int max = 7;
    for (int i = max - 1; i >= 0; i--) {
        int target = arc4random_uniform(i + 1);
        swap(objc_debug_tag60_permutations[i],
             objc_debug_tag60_permutations[target]);
    }
#endif
}

是赋随字符是什么机值。 接下多线程的实现方式来我们去源码里看看tag的值和代表的类型:

{
    // 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_NSColor           = 16,
    OBJC_TAG_UIColor           = 17,
    OBJC_TAG_CGColor           = 18,
    OBJC_TAG_NSIndexSet        = 19,
    OBJC_TAG_NSMethodSignature = 20,
    OBJC_TAG_UTTypeRecord      = 21,
    // When using the split tagged pointer representation
    // (OBJC_SPLIT_TAGGED_POINTERS), this is the first tag where
    // the tag and payload are unobfuscated. All tags from here to
    // OBJC_TAG_Last52BitPayload are unobfuscated. The shared cache
    // builder is able to construct these as long as the low bit is
    // not set (i.e. even-numbered tags).
    OBJC_TAG_FirstUnobfuscatedSplitTag = 136, // 128 + 8, first ext tag with high bit set
    OBJC_TAG_Constant_CFString = 136,
    OBJC_TAG_First60BitPayload = 0,
    OBJC_TAG_Last60BitPayload  = 6,
    OBJC_TAG_First52BitPayload = 8,
    OBJC_TAG_Last52BitPayload  = 263,
    OBJC_TAG_RESERVED_264      = 264
};

接下效率集来,我们使用系统的方法解析一下地址,按照,看一下它们代表的真实含义吧:源码

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        NSNumber *number0 = [NSNumber numberWithInt:0];
        NSNumber *number1 = [NSNumber numberWithInt:1];
        NSNumber *number2 = [NSNumber numberWithInt:2];
        NSNumber *number3 = [NSNumber numberWithInt:3];
        NSNumber *number4 = [NSNumber numberWithInt:4];
        NSNumber *number50 = [NSNumber numberWithInt:50];
#pragma mark number1
        NSLog(@"number1 pointer is %p---PointerValue:==0x%lx", number1, _objc_getTaggedPointerValue((__bridge void *)(number1)));
        NSLog(@"    decode:==0x%lx tag:%hu", _objc_decodeTaggedPointer((__bridge void *)(number1)), _objc_getTaggedPointerTag((__bridge void *)(number1)));
        NSLog(@"    %@", getBinaryByHex([NSString stringWithFormat:@"%lx", _objc_decodeTaggedPointer((__bridge void *)(number1))]));
#pragma mark number2
        NSLog(@"number2 pointer is %p---PointerValue:==0x%lx", number2, _objc_getTaggedPointerValue((__bridge void *)(number2)));
        NSLog(@"    decode:==0x%lx tag:%hu", _objc_decodeTaggedPointer((__bridge void *)(number2)), _objc_getTaggedPointerTag((__bridge void *)(number2)));
        NSLog(@"    %@", getBinaryByHex([NSString stringWithFormat:@"%lx", _objc_decodeTaggedPointer((__bridge void *)(number2))]));
#pragma mark number3
        NSLog(@"number3 pointer is %p---PointerValue:==0x%lx", number3, _objc_getTaggedPointerValue((__bridge void *)(number3)));
        NSLog(@"    decode:==0x%lx tag:%hu", _objc_decodeTaggedPointer((__bridge void *)(number3)), _objc_getTaggedPointerTag((__bridge void *)(number3)));
        NSLog(@"    %@", getBinaryByHex([NSString stringWithFormat:@"%lx", _objc_decodeTaggedPointer((__bridge void *)(number3))]));
#pragma mark number50
        NSLog(@"number50 pointer is %p---PointerValue:==0x%lx", number50, _objc_getTaggedPointerValue((__bridge void *)(number50)));
        NSLog(@"    decode:==0x%lx tag:%hu", _objc_decodeTaggedPointer((__bridge void *)(number50)), _objc_getTaggedPointerTag((__bridge void *)(number50)));
        NSLog(@"    %@", getBinaryByHex([NSString stringWithFormat:@"%lx", _objc_decodeTaggedPointer((__bridge void *)(number50))]));
        NSString *str1 = [NSString stringWithFormat:@"a"];
        NSString *str2 = [NSString stringWithFormat:@"ab"];
        NSString *str3 = [NSString stringWithFormat:@"abc"];
        NSString *str4 = [NSString stringWithFormat:@"abcf"];
#pragma mark str1
        NSLog(@"str1 pointer is %p---PointerValue:==0x%lx", str1, _objc_getTaggedPointerValue((__bridge void *)(str1)));
        NSLog(@"    %@", binForObjectPointer(str1));
        NSLog(@"    decode:==0x%lx tag:%hu", _objc_decodeTaggedPointer((__bridge void *)(str1)), _objc_getTaggedPointerTag((__bridge void *)(str1)));
        NSLog(@"    %@", getBinaryByHex([NSString stringWithFormat:@"%lx", _objc_decodeTaggedPointer((__bridge void *)(str1))]));
        NSLog(@"    %@", getBinaryByHex([NSString stringWithFormat:@"%p", str1]));
#pragma mark str2
        NSLog(@"str2 pointer is %p---PointerValue:==0x%lx", str2, _objc_getTaggedPointerValue((__bridge void *)(str2)));
        NSLog(@"    decode:==0x%lx tag:%hu", _objc_decodeTaggedPointer((__bridge void *)(str2)), _objc_getTaggedPointerTag((__bridge void *)(str2)));
        NSLog(@"    %@", getBinaryByHex([NSString stringWithFormat:@"%lx", _objc_decodeTaggedPointer((__bridge void *)(str2))]));
        NSLog(@"    %@", getBinaryByHex([NSString stringWithFormat:@"%p", str2]));
#pragma mark str3
        NSLog(@"str3 pointer is %p---PointerValue:==0x%lx", str3, _objc_getTaggedPointerValue((__bridge void *)(str3)));
        NSLog(@"    decode:==0x%lx tag:%hu", _objc_decodeTaggedPointer((__bridge void *)(str3)), _objc_getTaggedPointerTag((__bridge void *)(str3)));
        NSLog(@"    %@", getBinaryByHex([NSString stringWithFormat:@"%lx", _objc_decodeTaggedPointer((__bridge void *)(str3))]));
        NSLog(@"    %@", getBinaryByHex([NSString stringWithFormat:@"%p", str3]));
#pragma mark str4
        NSLog(@"str4 pointer is %p---PointerValue:==0x%lx", str4, _objc_getTaggedPointerValue((__bridge void *)(str4)));
        NSLog(@"    decode:==0x%lx tag:%hu", _objc_decodeTaggedPointer((__bridge void *)(str4)), _objc_getTaggedPointerTag((__bridge void *)(str4)));
        NSLog(@"    %@", getBinaryByHex([NSString stringWithFormat:@"%lx", _objc_decodeTaggedPointer((__bridge void *)(str4))]));
        NSLog(@"    %@", getBinaryByHex([NSString stringWithFormat:@"%p", str4]));
#pragma mark indexPath
        NSIndexPath *indexPath = [[NSIndexPath alloc] initWithIndex:4];
        NSLog(@"indexPath pointer is %p---PointerValue:==0x%lx", indexPath, _objc_getTaggedPointerValue((__bridge void *)(indexPath)));
        NSLog(@"    decode:==0x%lx tag:%hu", _objc_decodeTaggedPointer((__bridge void *)(indexPath)), _objc_getTaggedPointerTag((__bridge void *)(indexPath)));
        NSLog(@"    %@", getBinaryByHex([NSString stringWithFormat:@"%lx", _objc_decodeTaggedPointer((__bridge void *)(indexPath))]));
    }
    return 0;
}

输出:

源码编辑器下载r对象” alt=”[iOS开发]Tagged Pointer对字符型变量象” src=”https://www.6hu.cc/files/2022/06/260dd503b20e99f07a96aecb0f2f0fc6.p变量之间的关系ng”>
[iOS开发]Tagged Pointer对象源码1688“https://www.6hu.cc/files/2022/06/2f56a2c变量类型有哪些59e6bd1c7bee71b8ab4cddd60.png”> 可以看出,解析出来的tag就对应着它们的类型,看一下解析tag的方法:

static inline objc_tag_index_t
_objc_getTaggedPointerTag(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;
    uintptr_t extTag =   (value >> _OBJC_TAG_EXT_INDEX_SHIFT) & _OBJC_TAG_EXT_INDEX_MASK;
    if (basicTag == _OBJC_TAG_INDEX_MASK) {
        return (objc_tag_index_t)(extTag + OBJC_TAG_First52BitPayload);
    } else {
        return (objc_tag_index_t)basicTag;
    }
}

解析出来的pointerValue就代表着他们的值:NSNumber类后一位前的十六进制代表它们的真实值;NSS字符间距tring最后一位代表字符串长度,后面每两位代表多线程ASCII码值。 当8字节可以承载用于表示的数值时,系统就会以 Tagged Pointer 的方式生成指针,如果8字节承载不了时,则又用以前的方式来生成普通多线程是什么意思的指针。

特点

我们也可以看到苹果对于Tagged Pointer 特点的介绍:

  1. Tagg字符串逆序输出ed Pointer 专门用来存储小的对象,例如 NSNumberNS字符间距Date
  2. Tagged Pointer 指针的值不再是地址了,而是真正的值。所以,实际上它不再是一个对象

了,它只是一个披着对象“皮”的普通变量而己。所以,它的内存并不存储在堆中,也 不需要 mallocfree。 3. 在内存读取上有着以前3倍的效率,创建时比以前快 106倍

因此,苹果引入 Tagged Pointer,字符间距在哪里设置不但减少了 64位机器下程序的内存占用,还提高了运行效率,完美地解决了小内存对象在存储和访问效率上的问题。

注意事项

isa指针

Tagged Pointer 的引入也带来了问题字符间距在哪里设置,即 Tagged Pointer 并不是真正的对象,而是一个伪对象,所多线程的应用场景以你如果完全把它当成对象来使用,可能会让它“露马脚”。在上一章中我们写道,所有对象都有isa 指针,而 Tagged Pointer 其实是没有的,多线程是什么因为它不是真正的对象。

64位下的isa指针优化

对于 64位设备,苹果除了引人 Tagg效率符号ed Pointer 来优化小的对象外,对于普通的对象,其多线程的实现方式isa指针也进行了优化和调整。在32 位环境下,对象的引用计数都保存在一个外部的表中,每一个变量之间的关系对象的 Retain 操作,实际上包括如下 5个步骤:

  1. 获得全局的记录引用计数的hash 表。
  2. 为了线程安全,给该hash 表加锁。
  3. 查找到目标对效率公式象的引用计数值。
  4. 将该引用计数值加1,写回hash 表。
  5. 给该hash 表解锁。

从上面的步骤我们可以看出,为了保证源码交易平台线程安全,对引用计数的增减操作都要先锁定这个表,这从性能上看变量类型有哪些是非常差的。 而在 64 位环境下,isa 指针也是64 位,实际作为指针部分只用到的其中33位,剩余的 31位苹果使用了类似 Tagged Poin多线程并发中线程的状态ter 的概念,其中 19 位将保存对象的引1用计数,这样对引用计数的操作只需要修改源码这个指针即可。只有当引用计数超出19位,才会将引用计数保存到外部表,而这种情况是很少的,所以这样引用计数的更改效率会更高。 与前面的5个步骤对应,在64位字符间距怎么加宽环境下,新的 Ret多线程是什么ain 操作包括如下 5个步骤:

  1. 检查 isa 指针上面的标记位,看引1用计数是否保存在 isa变量中,如果不是,则使用以前

的步骤,否则执行第2步 2. 检查当前对象是源码编辑器下载否正在释放,如果是,则不做任何事情。 3. 增加该对象的变量类型有哪些引用计数,但是并不马上写回到isa 变量中。 4. 检杳增加后的引用计数的值是否能够被 19 位表示,如果不是,则切换成以前的办法,否 则执行第5步。 5. 进行一个原子的写操作,源码之家isa 的值写回。

虽然步骤都是 5步,但是由于没有了全局的加锁操作,所以引用源码编程器计数的更改更快了。

多线程并发中线程的状态inter对象” alt=”[iOS开发]Tagged Pointe多线程的应用场景r对多线程应用场景例子象” src=”https://www.6hu.多线程并发cc/wp-效率是什么意思content/uploads/2022/06/c89a0a609f126a1ed0f1多线程并发9259bc443c17.png”> 我们再来看看效率上的问题,为了存储和访问一个 NS源码精灵永久兑换码Number 对象,我们需要在堆上为其分配内存,另外还要维护它的引用计数,管理它的生命期。这些都给程序增加了额外的逻辑,造成运行效率上的损失。

Tagge效率英文翻译d Pointer介绍

Tagged Pointer

为了改进上面提到的内存占用和效率问题,苹效率是什么意思果提出了 Tagged Pointer 对象。由于 NSNumberNSDate一类的变量本身的值需要占源码编辑器用的内存大小常常不需要8个字节,拿整数来说,4个字节所能表示的有符号整数就可以达变量泵到20 多亿(注:2^31^ = 2147483648,另外1位作为符号位),对于绝大多数情况都是可以处理的。 所以我们可以将一个对象的指字符型变量针拆成两部分,一部分直接保存数据,另一部分作为特殊标记,表示这源码编辑器下载是一个特别的指针,不指向任何一个源码精灵永久兑换码地址。所字符串是什么意思以,引入了 Tagged Point变量值er 对象之后,64位 CPU 下 NSNumber 的内存图变成了字符型变量下面这样:

[iOS开发]Tagged Pointer对象多线程应用场景例子8345cf21f0.png”> 我们先看下下面这段代码:

dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_CONCURRENT);
for (int i = 0; i < 1000; i ++) {
	dispatch_async(queue, ^{
		self.name = [NSString stringWithFormat:@"abcdefghijklmn"];
	});
}
NSLog(@"end");

运行结果:崩溃(坏内存访问)

[iOS开发]Tagged Pointer对象 原因分析: 因为setter方法中,对strong修饰的属性会有一个retainrelease的操作。在并发多线程的赋值操作中,都是对_name指针进行的操作,可能在_name刚刚被release后进行赋值操作,这个时候_name指向的内存地址是已经被释放了,所以造成了坏内存访问崩溃。

- (void)setName:(NSString *)name {
    [name retain];
    [_name relase];
    _name = name;
}

解决办法:

  1. 将并发执效率计算公式行的任务改为串行执行
dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_SERIAL);
  1. 将异步执行改为同步执行
dispatch_sync(queue, ^{
 	self.name = [NSString stringWithFormat:@"abcdefghijklmn"];
});
  1. 将属性改为atomic属性原子性
  2. 加锁
myClass *test = [[myClass alloc] init];
NSLock *lock = [[NSLock alloc] init];
dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_CONCURRENT);
for (int i = 0; i < 1000; i ++) {
	dispatch_async(queue, ^{
		[lock lock];
		test.name = [NSString stringWithFormat:@"abcdefghijklmn"];
		[lock unlock];
	});
}
NSLog(@"end");

再看以下代码执行结果是什么,不崩溃了。因为没有用到引用计数的内存管理方法,使用源码编辑器下载的是Tagged Pointer

TaggedPointer 结构

苹果为了安全对源码编辑器下载其做了编码,runtime内变量类型有哪些部实现了编码、解码方法,我们变量与函数看一下: 编码:

static inline void * _Nonnull
_objc_encodeTaggedPointer(uintptr_t ptr)
{
    uintptr_t value = (objc_debug_taggedpointer_obfuscator ^ ptr);
#if OBJC_SPLIT_TAGGED_POINTERS
    if ((value & _OBJC_TAG_NO_OBFUSCATION_MASK) == _OBJC_TAG_NO_OBFUSCATION_MASK)
        return (void *)ptr;
    uintptr_t basicTag = (value >> _OBJC_TAG_INDEX_SHIFT) & _OBJC_TAG_INDEX_MASK;
    uintptr_t permutedTag = _objc_basicTagToObfuscatedTag(basicTag);
    value &= ~(_OBJC_TAG_INDEX_MASK << _OBJC_TAG_INDEX_SHIFT);
    value |= permutedTag << _OBJC_TAG_INDEX_SHIFT;
#endif
    return (void *)value;
}

里面进行了一系列的位运算。 我们可以试着打印地址:

NSNumber *number1 = [NSNumber numberWithInt:1];
NSLog(@"number1 pointer is %p", number1);

输出:

number1 pointer is 0xbb027d74df7d32ea

可见,这个地址是被编码过的效率是什么意思。通过资料的查询,我们可以对其结构有个了解:

  • Tagged Po效率集inter 标记:x86最后一位是标记位,arm64最高位是标记源码编辑器下载位。1表示是Tagged Pointer对象,0表示是普通对象。
  • Tag:对象类型标记。x86为1~3位,arm64为0~2。7表示有扩展信息。
  • Exten变量的定义ded:x86为4~11位,arm64为54~62变量名。用来扩展更多类型。
  • payload:有效负载。存储真正的数据(除了标记位、tag以及extended),不过为了安全苹果做字符串逆序输出了编码。

payload 代表有效负载,源码中有这样的注释:

[iOS开发]Tagged Pointer对象多线程应用场景例子rc=”https://www.6hu.cc/files/2022/06/931ed132d53d87dc7bc3728e3358b912.png”> 这里可以看出 需要经过 decod源码编程器ed_obj 加上一些位运算操作得到,说明是一个加密解密的变量类型有哪些过程,下面我们来看一下 decode

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;
}
static inline uintptr_t
_objc_decodeTaggedPointer(const void * _Nullable ptr)
{
    uintptr_t value = _objc_decodeTaggedPointer_noPermute(ptr);
#if OBJC_SPLIT_TAGGED_POINTERS
    uintptr_t basicTag = (value >> _OBJC_TAG_INDEX_SHIFT) & _OBJC_TAG_INDEX_MASK;
    value &= ~(_OBJC_TAG_INDEX_MASK << _OBJC_TAG_INDEX_SHIFT);
    value |= _objc_obfuscatedTagToBasicTag(basicTag) << _OBJC_TAG_INDEX_SHIFT;
#endif
    return value;
}

它是多线程应用场景例子由两个过程融合的。_objc_decodeTaggedPointer_noPermu字符是什么te()这个函数结束最后返回的 value 等于 value ^ objc_debug_taggedpoin变量与函数ter_obfusc字符串是什么意思ator,而 objc_debug_taggedpointer_obf源码编程器uscator 代表混淆,在initializeTaggedPointerObfuscator多线程应用场景例子()里可以看到它的初始变量化:

if (!DisableTaggedPointerObfuscation && dyld_program_sdk_at_least(dyld_fall_2018_os_versions)) {
    // 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;
#if OBJC_SPLIT_TAGGED_POINTERS
    // The obfuscator doesn't apply to any of the extended tag mask or the no-obfuscation bit.
    objc_debug_taggedpointer_obfuscator &= ~(_OBJC_TAG_EXT_MASK | _OBJC_TAG_NO_OBFUSCATION_MASK);
    // Shuffle the first seven entries of the tag permutator.
    int max = 7;
    for (int i = max - 1; i >= 0; i--) {
        int target = arc4random_uniform(i + 1);
        swap(objc_debug_tag60_permutations[i],
             objc_debug_tag60_permutations[target]);
    }
#endif
}

是赋随字符是什么机值。 接下多线程的实现方式来我们去源码里看看tag的值和代表的类型:

{
    // 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_NSColor           = 16,
    OBJC_TAG_UIColor           = 17,
    OBJC_TAG_CGColor           = 18,
    OBJC_TAG_NSIndexSet        = 19,
    OBJC_TAG_NSMethodSignature = 20,
    OBJC_TAG_UTTypeRecord      = 21,
    // When using the split tagged pointer representation
    // (OBJC_SPLIT_TAGGED_POINTERS), this is the first tag where
    // the tag and payload are unobfuscated. All tags from here to
    // OBJC_TAG_Last52BitPayload are unobfuscated. The shared cache
    // builder is able to construct these as long as the low bit is
    // not set (i.e. even-numbered tags).
    OBJC_TAG_FirstUnobfuscatedSplitTag = 136, // 128 + 8, first ext tag with high bit set
    OBJC_TAG_Constant_CFString = 136,
    OBJC_TAG_First60BitPayload = 0,
    OBJC_TAG_Last60BitPayload  = 6,
    OBJC_TAG_First52BitPayload = 8,
    OBJC_TAG_Last52BitPayload  = 263,
    OBJC_TAG_RESERVED_264      = 264
};

接下效率集来,我们使用系统的方法解析一下地址,按照,看一下它们代表的真实含义吧:源码

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        NSNumber *number0 = [NSNumber numberWithInt:0];
        NSNumber *number1 = [NSNumber numberWithInt:1];
        NSNumber *number2 = [NSNumber numberWithInt:2];
        NSNumber *number3 = [NSNumber numberWithInt:3];
        NSNumber *number4 = [NSNumber numberWithInt:4];
        NSNumber *number50 = [NSNumber numberWithInt:50];
#pragma mark number1
        NSLog(@"number1 pointer is %p---PointerValue:==0x%lx", number1, _objc_getTaggedPointerValue((__bridge void *)(number1)));
        NSLog(@"    decode:==0x%lx tag:%hu", _objc_decodeTaggedPointer((__bridge void *)(number1)), _objc_getTaggedPointerTag((__bridge void *)(number1)));
        NSLog(@"    %@", getBinaryByHex([NSString stringWithFormat:@"%lx", _objc_decodeTaggedPointer((__bridge void *)(number1))]));
#pragma mark number2
        NSLog(@"number2 pointer is %p---PointerValue:==0x%lx", number2, _objc_getTaggedPointerValue((__bridge void *)(number2)));
        NSLog(@"    decode:==0x%lx tag:%hu", _objc_decodeTaggedPointer((__bridge void *)(number2)), _objc_getTaggedPointerTag((__bridge void *)(number2)));
        NSLog(@"    %@", getBinaryByHex([NSString stringWithFormat:@"%lx", _objc_decodeTaggedPointer((__bridge void *)(number2))]));
#pragma mark number3
        NSLog(@"number3 pointer is %p---PointerValue:==0x%lx", number3, _objc_getTaggedPointerValue((__bridge void *)(number3)));
        NSLog(@"    decode:==0x%lx tag:%hu", _objc_decodeTaggedPointer((__bridge void *)(number3)), _objc_getTaggedPointerTag((__bridge void *)(number3)));
        NSLog(@"    %@", getBinaryByHex([NSString stringWithFormat:@"%lx", _objc_decodeTaggedPointer((__bridge void *)(number3))]));
#pragma mark number50
        NSLog(@"number50 pointer is %p---PointerValue:==0x%lx", number50, _objc_getTaggedPointerValue((__bridge void *)(number50)));
        NSLog(@"    decode:==0x%lx tag:%hu", _objc_decodeTaggedPointer((__bridge void *)(number50)), _objc_getTaggedPointerTag((__bridge void *)(number50)));
        NSLog(@"    %@", getBinaryByHex([NSString stringWithFormat:@"%lx", _objc_decodeTaggedPointer((__bridge void *)(number50))]));
        NSString *str1 = [NSString stringWithFormat:@"a"];
        NSString *str2 = [NSString stringWithFormat:@"ab"];
        NSString *str3 = [NSString stringWithFormat:@"abc"];
        NSString *str4 = [NSString stringWithFormat:@"abcf"];
#pragma mark str1
        NSLog(@"str1 pointer is %p---PointerValue:==0x%lx", str1, _objc_getTaggedPointerValue((__bridge void *)(str1)));
        NSLog(@"    %@", binForObjectPointer(str1));
        NSLog(@"    decode:==0x%lx tag:%hu", _objc_decodeTaggedPointer((__bridge void *)(str1)), _objc_getTaggedPointerTag((__bridge void *)(str1)));
        NSLog(@"    %@", getBinaryByHex([NSString stringWithFormat:@"%lx", _objc_decodeTaggedPointer((__bridge void *)(str1))]));
        NSLog(@"    %@", getBinaryByHex([NSString stringWithFormat:@"%p", str1]));
#pragma mark str2
        NSLog(@"str2 pointer is %p---PointerValue:==0x%lx", str2, _objc_getTaggedPointerValue((__bridge void *)(str2)));
        NSLog(@"    decode:==0x%lx tag:%hu", _objc_decodeTaggedPointer((__bridge void *)(str2)), _objc_getTaggedPointerTag((__bridge void *)(str2)));
        NSLog(@"    %@", getBinaryByHex([NSString stringWithFormat:@"%lx", _objc_decodeTaggedPointer((__bridge void *)(str2))]));
        NSLog(@"    %@", getBinaryByHex([NSString stringWithFormat:@"%p", str2]));
#pragma mark str3
        NSLog(@"str3 pointer is %p---PointerValue:==0x%lx", str3, _objc_getTaggedPointerValue((__bridge void *)(str3)));
        NSLog(@"    decode:==0x%lx tag:%hu", _objc_decodeTaggedPointer((__bridge void *)(str3)), _objc_getTaggedPointerTag((__bridge void *)(str3)));
        NSLog(@"    %@", getBinaryByHex([NSString stringWithFormat:@"%lx", _objc_decodeTaggedPointer((__bridge void *)(str3))]));
        NSLog(@"    %@", getBinaryByHex([NSString stringWithFormat:@"%p", str3]));
#pragma mark str4
        NSLog(@"str4 pointer is %p---PointerValue:==0x%lx", str4, _objc_getTaggedPointerValue((__bridge void *)(str4)));
        NSLog(@"    decode:==0x%lx tag:%hu", _objc_decodeTaggedPointer((__bridge void *)(str4)), _objc_getTaggedPointerTag((__bridge void *)(str4)));
        NSLog(@"    %@", getBinaryByHex([NSString stringWithFormat:@"%lx", _objc_decodeTaggedPointer((__bridge void *)(str4))]));
        NSLog(@"    %@", getBinaryByHex([NSString stringWithFormat:@"%p", str4]));
#pragma mark indexPath
        NSIndexPath *indexPath = [[NSIndexPath alloc] initWithIndex:4];
        NSLog(@"indexPath pointer is %p---PointerValue:==0x%lx", indexPath, _objc_getTaggedPointerValue((__bridge void *)(indexPath)));
        NSLog(@"    decode:==0x%lx tag:%hu", _objc_decodeTaggedPointer((__bridge void *)(indexPath)), _objc_getTaggedPointerTag((__bridge void *)(indexPath)));
        NSLog(@"    %@", getBinaryByHex([NSString stringWithFormat:@"%lx", _objc_decodeTaggedPointer((__bridge void *)(indexPath))]));
    }
    return 0;
}

输出:

源码编辑器下载r对象” alt=”[iOS开发]Tagged Pointer对字符型变量象” src=”https://www.6hu.cc/files/2022/06/260dd503b20e99f07a96aecb0f2f0fc6.p变量之间的关系ng”>
[iOS开发]Tagged Pointer对象源码1688“https://www.6hu.cc/files/2022/06/2f56a2c变量类型有哪些59e6bd1c7bee71b8ab4cddd60.png”> 可以看出,解析出来的tag就对应着它们的类型,看一下解析tag的方法:

static inline objc_tag_index_t
_objc_getTaggedPointerTag(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;
    uintptr_t extTag =   (value >> _OBJC_TAG_EXT_INDEX_SHIFT) & _OBJC_TAG_EXT_INDEX_MASK;
    if (basicTag == _OBJC_TAG_INDEX_MASK) {
        return (objc_tag_index_t)(extTag + OBJC_TAG_First52BitPayload);
    } else {
        return (objc_tag_index_t)basicTag;
    }
}

解析出来的pointerValue就代表着他们的值:NSNumber类后一位前的十六进制代表它们的真实值;NSS字符间距tring最后一位代表字符串长度,后面每两位代表多线程ASCII码值。 当8字节可以承载用于表示的数值时,系统就会以 Tagged Pointer 的方式生成指针,如果8字节承载不了时,则又用以前的方式来生成普通多线程是什么意思的指针。

特点

我们也可以看到苹果对于Tagged Pointer 特点的介绍:

  1. Tagg字符串逆序输出ed Pointer 专门用来存储小的对象,例如 NSNumberNS字符间距Date
  2. Tagged Pointer 指针的值不再是地址了,而是真正的值。所以,实际上它不再是一个对象

了,它只是一个披着对象“皮”的普通变量而己。所以,它的内存并不存储在堆中,也 不需要 mallocfree。 3. 在内存读取上有着以前3倍的效率,创建时比以前快 106倍

因此,苹果引入 Tagged Pointer,字符间距在哪里设置不但减少了 64位机器下程序的内存占用,还提高了运行效率,完美地解决了小内存对象在存储和访问效率上的问题。

注意事项

isa指针

Tagged Pointer 的引入也带来了问题字符间距在哪里设置,即 Tagged Pointer 并不是真正的对象,而是一个伪对象,所多线程的应用场景以你如果完全把它当成对象来使用,可能会让它“露马脚”。在上一章中我们写道,所有对象都有isa 指针,而 Tagged Pointer 其实是没有的,多线程是什么因为它不是真正的对象。

64位下的isa指针优化

对于 64位设备,苹果除了引人 Tagg效率符号ed Pointer 来优化小的对象外,对于普通的对象,其多线程的实现方式isa指针也进行了优化和调整。在32 位环境下,对象的引用计数都保存在一个外部的表中,每一个变量之间的关系对象的 Retain 操作,实际上包括如下 5个步骤:

  1. 获得全局的记录引用计数的hash 表。
  2. 为了线程安全,给该hash 表加锁。
  3. 查找到目标对效率公式象的引用计数值。
  4. 将该引用计数值加1,写回hash 表。
  5. 给该hash 表解锁。

从上面的步骤我们可以看出,为了保证源码交易平台线程安全,对引用计数的增减操作都要先锁定这个表,这从性能上看变量类型有哪些是非常差的。 而在 64 位环境下,isa 指针也是64 位,实际作为指针部分只用到的其中33位,剩余的 31位苹果使用了类似 Tagged Poin多线程并发中线程的状态ter 的概念,其中 19 位将保存对象的引1用计数,这样对引用计数的操作只需要修改源码这个指针即可。只有当引用计数超出19位,才会将引用计数保存到外部表,而这种情况是很少的,所以这样引用计数的更改效率会更高。 与前面的5个步骤对应,在64位字符间距怎么加宽环境下,新的 Ret多线程是什么ain 操作包括如下 5个步骤:

  1. 检查 isa 指针上面的标记位,看引1用计数是否保存在 isa变量中,如果不是,则使用以前

的步骤,否则执行第2步 2. 检查当前对象是源码编辑器下载否正在释放,如果是,则不做任何事情。 3. 增加该对象的变量类型有哪些引用计数,但是并不马上写回到isa 变量中。 4. 检杳增加后的引用计数的值是否能够被 19 位表示,如果不是,则切换成以前的办法,否 则执行第5步。 5. 进行一个原子的写操作,源码之家isa 的值写回。

虽然步骤都是 5步,但是由于没有了全局的加锁操作,所以引用源码编程器计数的更改更快了。

自2013年苹果推出iphone5s之后,iOS的寻址空间扩大到了64位。我们可以用63位来表示一个数字(一位做符号位)。那么这个数字的范围是2^6效率3 ,很明显我们一般不会用到这么大的数字,那么在我们定义一个数字时NSNum变量类型有哪些ber *多线程的应用场景num = @100,实际上内存中浪费了很多的内存空间。当然苹果肯定也认识到了这个问题,于是就引入了Tagged pointerTa变量名的命名规则gged pointer是一种特殊的“指针”,其特殊在于,其实它存储的并不是地址,而是真实的数据和一些附加的信息。

原有系统的问题

我们先看看原有的对象为什么会浪费内存。假设我们要存储一个 NSNumber 对象,其值是一个整数。字符间距加宽2磅怎么设置正常情况下,如果这个整数只是一个 N效率意识方面存在的问题SInteger 的普通变量,那么它所多线程的实现方式占用的内存与CPU 的位数有关,在32位 C源码中的图片PU 下占4个字节,在64位CPU 下是占8个字节的。而指 针类源码编辑器型的字符间距怎么加宽大小通常也与 CPU 位数相字符关,一个指针所占用的内存在32 位 CPU 下为4个字节,在64位 CPU 下是8个字节。 所以,效率的拼音一个普通的 iOS程序,如果没有 Tagged变量是什么意思 Pointer 对象,从32位机器迁移到 64位机器中,虽然逻辑没有任何变化,但这种 NSNumberNSDate一类的对象所占用的内存会翻倍。

多线程并发中线程的状态inter对象” alt=”[iOS开发]Tagged Pointe多线程的应用场景r对多线程应用场景例子象” src=”https://www.6hu.多线程并发cc/wp-效率是什么意思content/uploads/2022/06/c89a0a609f126a1ed0f1多线程并发9259bc443c17.png”> 我们再来看看效率上的问题,为了存储和访问一个 NS源码精灵永久兑换码Number 对象,我们需要在堆上为其分配内存,另外还要维护它的引用计数,管理它的生命期。这些都给程序增加了额外的逻辑,造成运行效率上的损失。

Tagge效率英文翻译d Pointer介绍

Tagged Pointer

为了改进上面提到的内存占用和效率问题,苹效率是什么意思果提出了 Tagged Pointer 对象。由于 NSNumberNSDate一类的变量本身的值需要占源码编辑器用的内存大小常常不需要8个字节,拿整数来说,4个字节所能表示的有符号整数就可以达变量泵到20 多亿(注:2^31^ = 2147483648,另外1位作为符号位),对于绝大多数情况都是可以处理的。 所以我们可以将一个对象的指字符型变量针拆成两部分,一部分直接保存数据,另一部分作为特殊标记,表示这源码编辑器下载是一个特别的指针,不指向任何一个源码精灵永久兑换码地址。所字符串是什么意思以,引入了 Tagged Point变量值er 对象之后,64位 CPU 下 NSNumber 的内存图变成了字符型变量下面这样:

[iOS开发]Tagged Pointer对象多线程应用场景例子8345cf21f0.png”> 我们先看下下面这段代码:

dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_CONCURRENT);
for (int i = 0; i < 1000; i ++) {
	dispatch_async(queue, ^{
		self.name = [NSString stringWithFormat:@"abcdefghijklmn"];
	});
}
NSLog(@"end");

运行结果:崩溃(坏内存访问)

[iOS开发]Tagged Pointer对象 原因分析: 因为setter方法中,对strong修饰的属性会有一个retainrelease的操作。在并发多线程的赋值操作中,都是对_name指针进行的操作,可能在_name刚刚被release后进行赋值操作,这个时候_name指向的内存地址是已经被释放了,所以造成了坏内存访问崩溃。

- (void)setName:(NSString *)name {
    [name retain];
    [_name relase];
    _name = name;
}

解决办法:

  1. 将并发执效率计算公式行的任务改为串行执行
dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_SERIAL);
  1. 将异步执行改为同步执行
dispatch_sync(queue, ^{
 	self.name = [NSString stringWithFormat:@"abcdefghijklmn"];
});
  1. 将属性改为atomic属性原子性
  2. 加锁
myClass *test = [[myClass alloc] init];
NSLock *lock = [[NSLock alloc] init];
dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_CONCURRENT);
for (int i = 0; i < 1000; i ++) {
	dispatch_async(queue, ^{
		[lock lock];
		test.name = [NSString stringWithFormat:@"abcdefghijklmn"];
		[lock unlock];
	});
}
NSLog(@"end");

再看以下代码执行结果是什么,不崩溃了。因为没有用到引用计数的内存管理方法,使用源码编辑器下载的是Tagged Pointer

TaggedPointer 结构

苹果为了安全对源码编辑器下载其做了编码,runtime内变量类型有哪些部实现了编码、解码方法,我们变量与函数看一下: 编码:

static inline void * _Nonnull
_objc_encodeTaggedPointer(uintptr_t ptr)
{
    uintptr_t value = (objc_debug_taggedpointer_obfuscator ^ ptr);
#if OBJC_SPLIT_TAGGED_POINTERS
    if ((value & _OBJC_TAG_NO_OBFUSCATION_MASK) == _OBJC_TAG_NO_OBFUSCATION_MASK)
        return (void *)ptr;
    uintptr_t basicTag = (value >> _OBJC_TAG_INDEX_SHIFT) & _OBJC_TAG_INDEX_MASK;
    uintptr_t permutedTag = _objc_basicTagToObfuscatedTag(basicTag);
    value &= ~(_OBJC_TAG_INDEX_MASK << _OBJC_TAG_INDEX_SHIFT);
    value |= permutedTag << _OBJC_TAG_INDEX_SHIFT;
#endif
    return (void *)value;
}

里面进行了一系列的位运算。 我们可以试着打印地址:

NSNumber *number1 = [NSNumber numberWithInt:1];
NSLog(@"number1 pointer is %p", number1);

输出:

number1 pointer is 0xbb027d74df7d32ea

可见,这个地址是被编码过的效率是什么意思。通过资料的查询,我们可以对其结构有个了解:

  • Tagged Po效率集inter 标记:x86最后一位是标记位,arm64最高位是标记源码编辑器下载位。1表示是Tagged Pointer对象,0表示是普通对象。
  • Tag:对象类型标记。x86为1~3位,arm64为0~2。7表示有扩展信息。
  • Exten变量的定义ded:x86为4~11位,arm64为54~62变量名。用来扩展更多类型。
  • payload:有效负载。存储真正的数据(除了标记位、tag以及extended),不过为了安全苹果做字符串逆序输出了编码。

payload 代表有效负载,源码中有这样的注释:

[iOS开发]Tagged Pointer对象多线程应用场景例子rc=”https://www.6hu.cc/files/2022/06/931ed132d53d87dc7bc3728e3358b912.png”> 这里可以看出 需要经过 decod源码编程器ed_obj 加上一些位运算操作得到,说明是一个加密解密的变量类型有哪些过程,下面我们来看一下 decode

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;
}
static inline uintptr_t
_objc_decodeTaggedPointer(const void * _Nullable ptr)
{
    uintptr_t value = _objc_decodeTaggedPointer_noPermute(ptr);
#if OBJC_SPLIT_TAGGED_POINTERS
    uintptr_t basicTag = (value >> _OBJC_TAG_INDEX_SHIFT) & _OBJC_TAG_INDEX_MASK;
    value &= ~(_OBJC_TAG_INDEX_MASK << _OBJC_TAG_INDEX_SHIFT);
    value |= _objc_obfuscatedTagToBasicTag(basicTag) << _OBJC_TAG_INDEX_SHIFT;
#endif
    return value;
}

它是多线程应用场景例子由两个过程融合的。_objc_decodeTaggedPointer_noPermu字符是什么te()这个函数结束最后返回的 value 等于 value ^ objc_debug_taggedpoin变量与函数ter_obfusc字符串是什么意思ator,而 objc_debug_taggedpointer_obf源码编程器uscator 代表混淆,在initializeTaggedPointerObfuscator多线程应用场景例子()里可以看到它的初始变量化:

if (!DisableTaggedPointerObfuscation && dyld_program_sdk_at_least(dyld_fall_2018_os_versions)) {
    // 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;
#if OBJC_SPLIT_TAGGED_POINTERS
    // The obfuscator doesn't apply to any of the extended tag mask or the no-obfuscation bit.
    objc_debug_taggedpointer_obfuscator &= ~(_OBJC_TAG_EXT_MASK | _OBJC_TAG_NO_OBFUSCATION_MASK);
    // Shuffle the first seven entries of the tag permutator.
    int max = 7;
    for (int i = max - 1; i >= 0; i--) {
        int target = arc4random_uniform(i + 1);
        swap(objc_debug_tag60_permutations[i],
             objc_debug_tag60_permutations[target]);
    }
#endif
}

是赋随字符是什么机值。 接下多线程的实现方式来我们去源码里看看tag的值和代表的类型:

{
    // 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_NSColor           = 16,
    OBJC_TAG_UIColor           = 17,
    OBJC_TAG_CGColor           = 18,
    OBJC_TAG_NSIndexSet        = 19,
    OBJC_TAG_NSMethodSignature = 20,
    OBJC_TAG_UTTypeRecord      = 21,
    // When using the split tagged pointer representation
    // (OBJC_SPLIT_TAGGED_POINTERS), this is the first tag where
    // the tag and payload are unobfuscated. All tags from here to
    // OBJC_TAG_Last52BitPayload are unobfuscated. The shared cache
    // builder is able to construct these as long as the low bit is
    // not set (i.e. even-numbered tags).
    OBJC_TAG_FirstUnobfuscatedSplitTag = 136, // 128 + 8, first ext tag with high bit set
    OBJC_TAG_Constant_CFString = 136,
    OBJC_TAG_First60BitPayload = 0,
    OBJC_TAG_Last60BitPayload  = 6,
    OBJC_TAG_First52BitPayload = 8,
    OBJC_TAG_Last52BitPayload  = 263,
    OBJC_TAG_RESERVED_264      = 264
};

接下效率集来,我们使用系统的方法解析一下地址,按照,看一下它们代表的真实含义吧:源码

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        NSNumber *number0 = [NSNumber numberWithInt:0];
        NSNumber *number1 = [NSNumber numberWithInt:1];
        NSNumber *number2 = [NSNumber numberWithInt:2];
        NSNumber *number3 = [NSNumber numberWithInt:3];
        NSNumber *number4 = [NSNumber numberWithInt:4];
        NSNumber *number50 = [NSNumber numberWithInt:50];
#pragma mark number1
        NSLog(@"number1 pointer is %p---PointerValue:==0x%lx", number1, _objc_getTaggedPointerValue((__bridge void *)(number1)));
        NSLog(@"    decode:==0x%lx tag:%hu", _objc_decodeTaggedPointer((__bridge void *)(number1)), _objc_getTaggedPointerTag((__bridge void *)(number1)));
        NSLog(@"    %@", getBinaryByHex([NSString stringWithFormat:@"%lx", _objc_decodeTaggedPointer((__bridge void *)(number1))]));
#pragma mark number2
        NSLog(@"number2 pointer is %p---PointerValue:==0x%lx", number2, _objc_getTaggedPointerValue((__bridge void *)(number2)));
        NSLog(@"    decode:==0x%lx tag:%hu", _objc_decodeTaggedPointer((__bridge void *)(number2)), _objc_getTaggedPointerTag((__bridge void *)(number2)));
        NSLog(@"    %@", getBinaryByHex([NSString stringWithFormat:@"%lx", _objc_decodeTaggedPointer((__bridge void *)(number2))]));
#pragma mark number3
        NSLog(@"number3 pointer is %p---PointerValue:==0x%lx", number3, _objc_getTaggedPointerValue((__bridge void *)(number3)));
        NSLog(@"    decode:==0x%lx tag:%hu", _objc_decodeTaggedPointer((__bridge void *)(number3)), _objc_getTaggedPointerTag((__bridge void *)(number3)));
        NSLog(@"    %@", getBinaryByHex([NSString stringWithFormat:@"%lx", _objc_decodeTaggedPointer((__bridge void *)(number3))]));
#pragma mark number50
        NSLog(@"number50 pointer is %p---PointerValue:==0x%lx", number50, _objc_getTaggedPointerValue((__bridge void *)(number50)));
        NSLog(@"    decode:==0x%lx tag:%hu", _objc_decodeTaggedPointer((__bridge void *)(number50)), _objc_getTaggedPointerTag((__bridge void *)(number50)));
        NSLog(@"    %@", getBinaryByHex([NSString stringWithFormat:@"%lx", _objc_decodeTaggedPointer((__bridge void *)(number50))]));
        NSString *str1 = [NSString stringWithFormat:@"a"];
        NSString *str2 = [NSString stringWithFormat:@"ab"];
        NSString *str3 = [NSString stringWithFormat:@"abc"];
        NSString *str4 = [NSString stringWithFormat:@"abcf"];
#pragma mark str1
        NSLog(@"str1 pointer is %p---PointerValue:==0x%lx", str1, _objc_getTaggedPointerValue((__bridge void *)(str1)));
        NSLog(@"    %@", binForObjectPointer(str1));
        NSLog(@"    decode:==0x%lx tag:%hu", _objc_decodeTaggedPointer((__bridge void *)(str1)), _objc_getTaggedPointerTag((__bridge void *)(str1)));
        NSLog(@"    %@", getBinaryByHex([NSString stringWithFormat:@"%lx", _objc_decodeTaggedPointer((__bridge void *)(str1))]));
        NSLog(@"    %@", getBinaryByHex([NSString stringWithFormat:@"%p", str1]));
#pragma mark str2
        NSLog(@"str2 pointer is %p---PointerValue:==0x%lx", str2, _objc_getTaggedPointerValue((__bridge void *)(str2)));
        NSLog(@"    decode:==0x%lx tag:%hu", _objc_decodeTaggedPointer((__bridge void *)(str2)), _objc_getTaggedPointerTag((__bridge void *)(str2)));
        NSLog(@"    %@", getBinaryByHex([NSString stringWithFormat:@"%lx", _objc_decodeTaggedPointer((__bridge void *)(str2))]));
        NSLog(@"    %@", getBinaryByHex([NSString stringWithFormat:@"%p", str2]));
#pragma mark str3
        NSLog(@"str3 pointer is %p---PointerValue:==0x%lx", str3, _objc_getTaggedPointerValue((__bridge void *)(str3)));
        NSLog(@"    decode:==0x%lx tag:%hu", _objc_decodeTaggedPointer((__bridge void *)(str3)), _objc_getTaggedPointerTag((__bridge void *)(str3)));
        NSLog(@"    %@", getBinaryByHex([NSString stringWithFormat:@"%lx", _objc_decodeTaggedPointer((__bridge void *)(str3))]));
        NSLog(@"    %@", getBinaryByHex([NSString stringWithFormat:@"%p", str3]));
#pragma mark str4
        NSLog(@"str4 pointer is %p---PointerValue:==0x%lx", str4, _objc_getTaggedPointerValue((__bridge void *)(str4)));
        NSLog(@"    decode:==0x%lx tag:%hu", _objc_decodeTaggedPointer((__bridge void *)(str4)), _objc_getTaggedPointerTag((__bridge void *)(str4)));
        NSLog(@"    %@", getBinaryByHex([NSString stringWithFormat:@"%lx", _objc_decodeTaggedPointer((__bridge void *)(str4))]));
        NSLog(@"    %@", getBinaryByHex([NSString stringWithFormat:@"%p", str4]));
#pragma mark indexPath
        NSIndexPath *indexPath = [[NSIndexPath alloc] initWithIndex:4];
        NSLog(@"indexPath pointer is %p---PointerValue:==0x%lx", indexPath, _objc_getTaggedPointerValue((__bridge void *)(indexPath)));
        NSLog(@"    decode:==0x%lx tag:%hu", _objc_decodeTaggedPointer((__bridge void *)(indexPath)), _objc_getTaggedPointerTag((__bridge void *)(indexPath)));
        NSLog(@"    %@", getBinaryByHex([NSString stringWithFormat:@"%lx", _objc_decodeTaggedPointer((__bridge void *)(indexPath))]));
    }
    return 0;
}

输出:

源码编辑器下载r对象” alt=”[iOS开发]Tagged Pointer对字符型变量象” src=”https://www.6hu.cc/files/2022/06/260dd503b20e99f07a96aecb0f2f0fc6.p变量之间的关系ng”>
[iOS开发]Tagged Pointer对象源码1688“https://www.6hu.cc/files/2022/06/2f56a2c变量类型有哪些59e6bd1c7bee71b8ab4cddd60.png”> 可以看出,解析出来的tag就对应着它们的类型,看一下解析tag的方法:

static inline objc_tag_index_t
_objc_getTaggedPointerTag(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;
    uintptr_t extTag =   (value >> _OBJC_TAG_EXT_INDEX_SHIFT) & _OBJC_TAG_EXT_INDEX_MASK;
    if (basicTag == _OBJC_TAG_INDEX_MASK) {
        return (objc_tag_index_t)(extTag + OBJC_TAG_First52BitPayload);
    } else {
        return (objc_tag_index_t)basicTag;
    }
}

解析出来的pointerValue就代表着他们的值:NSNumber类后一位前的十六进制代表它们的真实值;NSS字符间距tring最后一位代表字符串长度,后面每两位代表多线程ASCII码值。 当8字节可以承载用于表示的数值时,系统就会以 Tagged Pointer 的方式生成指针,如果8字节承载不了时,则又用以前的方式来生成普通多线程是什么意思的指针。

特点

我们也可以看到苹果对于Tagged Pointer 特点的介绍:

  1. Tagg字符串逆序输出ed Pointer 专门用来存储小的对象,例如 NSNumberNS字符间距Date
  2. Tagged Pointer 指针的值不再是地址了,而是真正的值。所以,实际上它不再是一个对象

了,它只是一个披着对象“皮”的普通变量而己。所以,它的内存并不存储在堆中,也 不需要 mallocfree。 3. 在内存读取上有着以前3倍的效率,创建时比以前快 106倍

因此,苹果引入 Tagged Pointer,字符间距在哪里设置不但减少了 64位机器下程序的内存占用,还提高了运行效率,完美地解决了小内存对象在存储和访问效率上的问题。

注意事项

isa指针

Tagged Pointer 的引入也带来了问题字符间距在哪里设置,即 Tagged Pointer 并不是真正的对象,而是一个伪对象,所多线程的应用场景以你如果完全把它当成对象来使用,可能会让它“露马脚”。在上一章中我们写道,所有对象都有isa 指针,而 Tagged Pointer 其实是没有的,多线程是什么因为它不是真正的对象。

64位下的isa指针优化

对于 64位设备,苹果除了引人 Tagg效率符号ed Pointer 来优化小的对象外,对于普通的对象,其多线程的实现方式isa指针也进行了优化和调整。在32 位环境下,对象的引用计数都保存在一个外部的表中,每一个变量之间的关系对象的 Retain 操作,实际上包括如下 5个步骤:

  1. 获得全局的记录引用计数的hash 表。
  2. 为了线程安全,给该hash 表加锁。
  3. 查找到目标对效率公式象的引用计数值。
  4. 将该引用计数值加1,写回hash 表。
  5. 给该hash 表解锁。

从上面的步骤我们可以看出,为了保证源码交易平台线程安全,对引用计数的增减操作都要先锁定这个表,这从性能上看变量类型有哪些是非常差的。 而在 64 位环境下,isa 指针也是64 位,实际作为指针部分只用到的其中33位,剩余的 31位苹果使用了类似 Tagged Poin多线程并发中线程的状态ter 的概念,其中 19 位将保存对象的引1用计数,这样对引用计数的操作只需要修改源码这个指针即可。只有当引用计数超出19位,才会将引用计数保存到外部表,而这种情况是很少的,所以这样引用计数的更改效率会更高。 与前面的5个步骤对应,在64位字符间距怎么加宽环境下,新的 Ret多线程是什么ain 操作包括如下 5个步骤:

  1. 检查 isa 指针上面的标记位,看引1用计数是否保存在 isa变量中,如果不是,则使用以前

的步骤,否则执行第2步 2. 检查当前对象是源码编辑器下载否正在释放,如果是,则不做任何事情。 3. 增加该对象的变量类型有哪些引用计数,但是并不马上写回到isa 变量中。 4. 检杳增加后的引用计数的值是否能够被 19 位表示,如果不是,则切换成以前的办法,否 则执行第5步。 5. 进行一个原子的写操作,源码之家isa 的值写回。

虽然步骤都是 5步,但是由于没有了全局的加锁操作,所以引用源码编程器计数的更改更快了。