源码编辑器下载r对象” alt=”[iOS开发]Tagged Pointer对字符型变量 象” src=”https://www.6hu.cc/wp-content/uploads/2022/06/260dd503b20e99f07a96aecb0f2f0fc6.p变量之间的关系 ng”>
源码1688“https://www.6hu.cc/wp-content/uploads/2022/06/2f56a2c变量类型有哪些 59e6bd1c7bee71b8ab4cddd60.png”>
可以看出,解析出来的tag就对应着它们的类型,看一下解析tag的方法:
static inline objc_tag_index_t
_objc_getTaggedPointerTag(const void * _Nullable 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 特点的介绍:
Tagg字符串逆序输出 ed Pointer 专门用来存储小的对象,例如 NSNumber 和 NS字符间距 Date。
Tagged Pointer 指针的值不再是地址了,而是真正的值。所以,实际上它不再是一个对象
了,它只是一个披着对象“皮”的普通变量而己。所以,它的内存并不存储在堆中,也
不需要 malloc 和free。
3. 在内存读取上有着以前3倍的效率,创建时比以前快 106倍
因此,苹果引入 Tagged Pointer,字符间距在哪里设置 不但减少了 64位机器下程序的内存占用,还提高了运行效率,完美地解决了小内存对象在存储和访问效率上的问题。
注意事项
isa指针
Tagged Pointer 的引入也带来了问题字符间距在哪里设置 ,即 Tagged Pointer 并不是真正的对象,而是一个伪对象,所多线程的应用场景 以你如果完全把它当成对象来使用,可能会让它“露马脚”。在上一章中我们写道,所有对象都有isa 指针,而 Tagged Pointer 其实是没有的,多线程是什么 因为它不是真正的对象。
64位下的isa指针优化
对于 64位设备,苹果除了引人 Tagg效率符号 ed Pointer 来优化小的对象外,对于普通的对象,其多线程的实现方式 isa指针也进行了优化和调整。在32 位环境下,对象的引用计数都保存在一个外部的表中,每一个变量之间的关系 对象的 Retain 操作,实际上包括如下 5个步骤:
获得全局的记录引用计数的hash 表。
为了线程安全,给该hash 表加锁。
查找到目标对效率公式 象的引用计数值。
将该引用计数值加1,写回hash 表。
给该hash 表解锁。
从上面的步骤我们可以看出,为了保证源码交易平台 线程安全,对引用计数的增减操作都要先锁定这个表,这从性能上看变量类型有哪些 是非常差的。
而在 64 位环境下,isa 指针也是64 位,实际作为指针部分只用到的其中33位,剩余的 31位苹果使用了类似 Tagged Poin多线程并发中线程的状态 ter 的概念,其中 19 位将保存对象的引1用计数,这样对引用计数的操作只需要修改源码 这个指针即可。只有当引用计数超出19位,才会将引用计数保存到外部表,而这种情况是很少的,所以这样引用计数的更改效率会更高。
与前面的5个步骤对应,在64位字符间距怎么加宽 环境下,新的 Ret多线程是什么 ain 操作包括如下 5个步骤:
检查 isa 指针上面的标记位,看引1用计数是否保存在 isa变量中,如果不是,则使用以前
的步骤,否则执行第2步
2. 检查当前对象是源码编辑器下载 否正在释放,如果是,则不做任何事情。
3. 增加该对象的变量类型有哪些 引用计数,但是并不马上写回到isa 变量中。
4. 检杳增加后的引用计数的值是否能够被 19 位表示,如果不是,则切换成以前的办法,否
则执行第5步。
5. 进行一个原子的写操作,源码之家 将isa 的值写回。
虽然步骤都是 5步,但是由于没有了全局的加锁操作,所以引用源码编程器 计数的更改更快了。
原因分析:
因为setter方法中,对strong修饰的属性会有一个retain和release的操作。在并发多线程的赋值操作中,都是对_name指针进行的操作,可能在_name刚刚被release后进行赋值操作,这个时候_name指向的内存地址是已经被释放了,所以造成了坏内存访问崩溃。
- (void )setName:(NSString *)name {
[name retain ];
[_name relase];
_name = name;
}
解决办法:
将并发执效率计算公式 行的任务改为串行执行
dispatch_queue_t queue = dispatch_queue_create("queue" , DISPATCH_QUEUE_SERIAL);
将异步执行改为同步执行
dispatch_sync (queue, ^{
self .name = [NSString stringWithFormat:@"abcdefghijklmn" ];
});
将属性改为atomic属性原子性
加锁
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 代表有效负载,源码中有这样的注释:
多线程应用场景例子rc=”https://www.6hu.cc/wp-content/uploads/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)) {
arc4random_buf(&objc_debug_taggedpointer_obfuscator,
sizeof (objc_debug_taggedpointer_obfuscator));
objc_debug_taggedpointer_obfuscator &= ~_OBJC_TAG_MASK;
#if OBJC_SPLIT_TAGGED_POINTERS
objc_debug_taggedpointer_obfuscator &= ~(_OBJC_TAG_EXT_MASK | _OBJC_TAG_NO_OBFUSCATION_MASK);
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的值和代表的类型:
{
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 ,
OBJC_TAG_RESERVED_7 = 7 ,
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 ,
OBJC_TAG_FirstUnobfuscatedSplitTag = 136 ,
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/wp-content/uploads/2022/06/260dd503b20e99f07a96aecb0f2f0fc6.p变量之间的关系 ng”>
源码1688“https://www.6hu.cc/wp-content/uploads/2022/06/2f56a2c变量类型有哪些 59e6bd1c7bee71b8ab4cddd60.png”>
可以看出,解析出来的tag就对应着它们的类型,看一下解析tag的方法:
static inline objc_tag_index_t
_objc_getTaggedPointerTag(const void * _Nullable 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 特点的介绍:
Tagg字符串逆序输出 ed Pointer 专门用来存储小的对象,例如 NSNumber 和 NS字符间距 Date。
Tagged Pointer 指针的值不再是地址了,而是真正的值。所以,实际上它不再是一个对象
了,它只是一个披着对象“皮”的普通变量而己。所以,它的内存并不存储在堆中,也
不需要 malloc 和free。
3. 在内存读取上有着以前3倍的效率,创建时比以前快 106倍
因此,苹果引入 Tagged Pointer,字符间距在哪里设置 不但减少了 64位机器下程序的内存占用,还提高了运行效率,完美地解决了小内存对象在存储和访问效率上的问题。
注意事项
isa指针
Tagged Pointer 的引入也带来了问题字符间距在哪里设置 ,即 Tagged Pointer 并不是真正的对象,而是一个伪对象,所多线程的应用场景 以你如果完全把它当成对象来使用,可能会让它“露马脚”。在上一章中我们写道,所有对象都有isa 指针,而 Tagged Pointer 其实是没有的,多线程是什么 因为它不是真正的对象。
64位下的isa指针优化
对于 64位设备,苹果除了引人 Tagg效率符号 ed Pointer 来优化小的对象外,对于普通的对象,其多线程的实现方式 isa指针也进行了优化和调整。在32 位环境下,对象的引用计数都保存在一个外部的表中,每一个变量之间的关系 对象的 Retain 操作,实际上包括如下 5个步骤:
获得全局的记录引用计数的hash 表。
为了线程安全,给该hash 表加锁。
查找到目标对效率公式 象的引用计数值。
将该引用计数值加1,写回hash 表。
给该hash 表解锁。
从上面的步骤我们可以看出,为了保证源码交易平台 线程安全,对引用计数的增减操作都要先锁定这个表,这从性能上看变量类型有哪些 是非常差的。
而在 64 位环境下,isa 指针也是64 位,实际作为指针部分只用到的其中33位,剩余的 31位苹果使用了类似 Tagged Poin多线程并发中线程的状态 ter 的概念,其中 19 位将保存对象的引1用计数,这样对引用计数的操作只需要修改源码 这个指针即可。只有当引用计数超出19位,才会将引用计数保存到外部表,而这种情况是很少的,所以这样引用计数的更改效率会更高。
与前面的5个步骤对应,在64位字符间距怎么加宽 环境下,新的 Ret多线程是什么 ain 操作包括如下 5个步骤:
检查 isa 指针上面的标记位,看引1用计数是否保存在 isa变量中,如果不是,则使用以前
的步骤,否则执行第2步
2. 检查当前对象是源码编辑器下载 否正在释放,如果是,则不做任何事情。
3. 增加该对象的变量类型有哪些 引用计数,但是并不马上写回到isa 变量中。
4. 检杳增加后的引用计数的值是否能够被 19 位表示,如果不是,则切换成以前的办法,否
则执行第5步。
5. 进行一个原子的写操作,源码之家 将isa 的值写回。
虽然步骤都是 5步,但是由于没有了全局的加锁操作,所以引用源码编程器 计数的更改更快了。
多线程应用场景例子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" );
运行结果:崩溃(坏内存访问)
原因分析:
因为setter方法中,对strong修饰的属性会有一个retain和release的操作。在并发多线程的赋值操作中,都是对_name指针进行的操作,可能在_name刚刚被release后进行赋值操作,这个时候_name指向的内存地址是已经被释放了,所以造成了坏内存访问崩溃。
- (void )setName:(NSString *)name {
[name retain ];
[_name relase];
_name = name;
}
解决办法:
将并发执效率计算公式 行的任务改为串行执行
dispatch_queue_t queue = dispatch_queue_create("queue" , DISPATCH_QUEUE_SERIAL);
将异步执行改为同步执行
dispatch_sync (queue, ^{
self .name = [NSString stringWithFormat:@"abcdefghijklmn" ];
});
将属性改为atomic属性原子性
加锁
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 代表有效负载,源码中有这样的注释:
多线程应用场景例子rc=”https://www.6hu.cc/wp-content/uploads/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)) {
arc4random_buf(&objc_debug_taggedpointer_obfuscator,
sizeof (objc_debug_taggedpointer_obfuscator));
objc_debug_taggedpointer_obfuscator &= ~_OBJC_TAG_MASK;
#if OBJC_SPLIT_TAGGED_POINTERS
objc_debug_taggedpointer_obfuscator &= ~(_OBJC_TAG_EXT_MASK | _OBJC_TAG_NO_OBFUSCATION_MASK);
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的值和代表的类型:
{
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 ,
OBJC_TAG_RESERVED_7 = 7 ,
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 ,
OBJC_TAG_FirstUnobfuscatedSplitTag = 136 ,
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/wp-content/uploads/2022/06/260dd503b20e99f07a96aecb0f2f0fc6.p变量之间的关系 ng”>
源码1688“https://www.6hu.cc/wp-content/uploads/2022/06/2f56a2c变量类型有哪些 59e6bd1c7bee71b8ab4cddd60.png”>
可以看出,解析出来的tag就对应着它们的类型,看一下解析tag的方法:
static inline objc_tag_index_t
_objc_getTaggedPointerTag(const void * _Nullable 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 特点的介绍:
Tagg字符串逆序输出 ed Pointer 专门用来存储小的对象,例如 NSNumber 和 NS字符间距 Date。
Tagged Pointer 指针的值不再是地址了,而是真正的值。所以,实际上它不再是一个对象
了,它只是一个披着对象“皮”的普通变量而己。所以,它的内存并不存储在堆中,也
不需要 malloc 和free。
3. 在内存读取上有着以前3倍的效率,创建时比以前快 106倍
因此,苹果引入 Tagged Pointer,字符间距在哪里设置 不但减少了 64位机器下程序的内存占用,还提高了运行效率,完美地解决了小内存对象在存储和访问效率上的问题。
注意事项
isa指针
Tagged Pointer 的引入也带来了问题字符间距在哪里设置 ,即 Tagged Pointer 并不是真正的对象,而是一个伪对象,所多线程的应用场景 以你如果完全把它当成对象来使用,可能会让它“露马脚”。在上一章中我们写道,所有对象都有isa 指针,而 Tagged Pointer 其实是没有的,多线程是什么 因为它不是真正的对象。
64位下的isa指针优化
对于 64位设备,苹果除了引人 Tagg效率符号 ed Pointer 来优化小的对象外,对于普通的对象,其多线程的实现方式 isa指针也进行了优化和调整。在32 位环境下,对象的引用计数都保存在一个外部的表中,每一个变量之间的关系 对象的 Retain 操作,实际上包括如下 5个步骤:
获得全局的记录引用计数的hash 表。
为了线程安全,给该hash 表加锁。
查找到目标对效率公式 象的引用计数值。
将该引用计数值加1,写回hash 表。
给该hash 表解锁。
从上面的步骤我们可以看出,为了保证源码交易平台 线程安全,对引用计数的增减操作都要先锁定这个表,这从性能上看变量类型有哪些 是非常差的。
而在 64 位环境下,isa 指针也是64 位,实际作为指针部分只用到的其中33位,剩余的 31位苹果使用了类似 Tagged Poin多线程并发中线程的状态 ter 的概念,其中 19 位将保存对象的引1用计数,这样对引用计数的操作只需要修改源码 这个指针即可。只有当引用计数超出19位,才会将引用计数保存到外部表,而这种情况是很少的,所以这样引用计数的更改效率会更高。
与前面的5个步骤对应,在64位字符间距怎么加宽 环境下,新的 Ret多线程是什么 ain 操作包括如下 5个步骤:
检查 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 对象。由于 NSNumber、NSDate一类的变量本身的值需要占源码编辑器 用的内存大小常常不需要8个字节,拿整数来说,4个字节所能表示的有符号整数就可以达变量泵 到20 多亿(注:2^31^ = 2147483648,另外1位作为符号位),对于绝大多数情况都是可以处理的。
所以我们可以将一个对象的指字符型变量 针拆成两部分,一部分直接保存数据,另一部分作为特殊标记,表示这源码编辑器下载 是一个特别的指针,不指向任何一个源码精灵永久兑换码 地址。所字符串是什么意思 以,引入了 Tagged Point变量值 er 对象之后,64位 CPU 下 NSNumber 的内存图变成了字符型变量 下面这样:
多线程应用场景例子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" );
运行结果:崩溃(坏内存访问)
原因分析:
因为setter方法中,对strong修饰的属性会有一个retain和release的操作。在并发多线程的赋值操作中,都是对_name指针进行的操作,可能在_name刚刚被release后进行赋值操作,这个时候_name指向的内存地址是已经被释放了,所以造成了坏内存访问崩溃。
- (void )setName:(NSString *)name {
[name retain ];
[_name relase];
_name = name;
}
解决办法:
将并发执效率计算公式 行的任务改为串行执行
dispatch_queue_t queue = dispatch_queue_create("queue" , DISPATCH_QUEUE_SERIAL);
将异步执行改为同步执行
dispatch_sync (queue, ^{
self .name = [NSString stringWithFormat:@"abcdefghijklmn" ];
});
将属性改为atomic属性原子性
加锁
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 代表有效负载,源码中有这样的注释:
多线程应用场景例子rc=”https://www.6hu.cc/wp-content/uploads/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)) {
arc4random_buf(&objc_debug_taggedpointer_obfuscator,
sizeof (objc_debug_taggedpointer_obfuscator));
objc_debug_taggedpointer_obfuscator &= ~_OBJC_TAG_MASK;
#if OBJC_SPLIT_TAGGED_POINTERS
objc_debug_taggedpointer_obfuscator &= ~(_OBJC_TAG_EXT_MASK | _OBJC_TAG_NO_OBFUSCATION_MASK);
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的值和代表的类型:
{
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 ,
OBJC_TAG_RESERVED_7 = 7 ,
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 ,
OBJC_TAG_FirstUnobfuscatedSplitTag = 136 ,
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/wp-content/uploads/2022/06/260dd503b20e99f07a96aecb0f2f0fc6.p变量之间的关系 ng”>
源码1688“https://www.6hu.cc/wp-content/uploads/2022/06/2f56a2c变量类型有哪些 59e6bd1c7bee71b8ab4cddd60.png”>
可以看出,解析出来的tag就对应着它们的类型,看一下解析tag的方法:
static inline objc_tag_index_t
_objc_getTaggedPointerTag(const void * _Nullable 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 特点的介绍:
Tagg字符串逆序输出 ed Pointer 专门用来存储小的对象,例如 NSNumber 和 NS字符间距 Date。
Tagged Pointer 指针的值不再是地址了,而是真正的值。所以,实际上它不再是一个对象
了,它只是一个披着对象“皮”的普通变量而己。所以,它的内存并不存储在堆中,也
不需要 malloc 和free。
3. 在内存读取上有着以前3倍的效率,创建时比以前快 106倍
因此,苹果引入 Tagged Pointer,字符间距在哪里设置 不但减少了 64位机器下程序的内存占用,还提高了运行效率,完美地解决了小内存对象在存储和访问效率上的问题。
注意事项
isa指针
Tagged Pointer 的引入也带来了问题字符间距在哪里设置 ,即 Tagged Pointer 并不是真正的对象,而是一个伪对象,所多线程的应用场景 以你如果完全把它当成对象来使用,可能会让它“露马脚”。在上一章中我们写道,所有对象都有isa 指针,而 Tagged Pointer 其实是没有的,多线程是什么 因为它不是真正的对象。
64位下的isa指针优化
对于 64位设备,苹果除了引人 Tagg效率符号 ed Pointer 来优化小的对象外,对于普通的对象,其多线程的实现方式 isa指针也进行了优化和调整。在32 位环境下,对象的引用计数都保存在一个外部的表中,每一个变量之间的关系 对象的 Retain 操作,实际上包括如下 5个步骤:
获得全局的记录引用计数的hash 表。
为了线程安全,给该hash 表加锁。
查找到目标对效率公式 象的引用计数值。
将该引用计数值加1,写回hash 表。
给该hash 表解锁。
从上面的步骤我们可以看出,为了保证源码交易平台 线程安全,对引用计数的增减操作都要先锁定这个表,这从性能上看变量类型有哪些 是非常差的。
而在 64 位环境下,isa 指针也是64 位,实际作为指针部分只用到的其中33位,剩余的 31位苹果使用了类似 Tagged Poin多线程并发中线程的状态 ter 的概念,其中 19 位将保存对象的引1用计数,这样对引用计数的操作只需要修改源码 这个指针即可。只有当引用计数超出19位,才会将引用计数保存到外部表,而这种情况是很少的,所以这样引用计数的更改效率会更高。
与前面的5个步骤对应,在64位字符间距怎么加宽 环境下,新的 Ret多线程是什么 ain 操作包括如下 5个步骤:
检查 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 pointer,Ta变量名的命名规则 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位机器中,虽然逻辑没有任何变化,但这种 NSNumber、 NSDate一类的对象所占用的内存会翻倍。
多线程并发中线程的状态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 对象。由于 NSNumber、NSDate一类的变量本身的值需要占源码编辑器 用的内存大小常常不需要8个字节,拿整数来说,4个字节所能表示的有符号整数就可以达变量泵 到20 多亿(注:2^31^ = 2147483648,另外1位作为符号位),对于绝大多数情况都是可以处理的。
所以我们可以将一个对象的指字符型变量 针拆成两部分,一部分直接保存数据,另一部分作为特殊标记,表示这源码编辑器下载 是一个特别的指针,不指向任何一个源码精灵永久兑换码 地址。所字符串是什么意思 以,引入了 Tagged Point变量值 er 对象之后,64位 CPU 下 NSNumber 的内存图变成了字符型变量 下面这样:
多线程应用场景例子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" );
运行结果:崩溃(坏内存访问)
原因分析:
因为setter方法中,对strong修饰的属性会有一个retain和release的操作。在并发多线程的赋值操作中,都是对_name指针进行的操作,可能在_name刚刚被release后进行赋值操作,这个时候_name指向的内存地址是已经被释放了,所以造成了坏内存访问崩溃。
- (void )setName:(NSString *)name {
[name retain ];
[_name relase];
_name = name;
}
解决办法:
将并发执效率计算公式 行的任务改为串行执行
dispatch_queue_t queue = dispatch_queue_create("queue" , DISPATCH_QUEUE_SERIAL);
将异步执行改为同步执行
dispatch_sync (queue, ^{
self .name = [NSString stringWithFormat:@"abcdefghijklmn" ];
});
将属性改为atomic属性原子性
加锁
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 代表有效负载,源码中有这样的注释:
多线程应用场景例子rc=”https://www.6hu.cc/wp-content/uploads/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)) {
arc4random_buf(&objc_debug_taggedpointer_obfuscator,
sizeof (objc_debug_taggedpointer_obfuscator));
objc_debug_taggedpointer_obfuscator &= ~_OBJC_TAG_MASK;
#if OBJC_SPLIT_TAGGED_POINTERS
objc_debug_taggedpointer_obfuscator &= ~(_OBJC_TAG_EXT_MASK | _OBJC_TAG_NO_OBFUSCATION_MASK);
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的值和代表的类型:
{
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 ,
OBJC_TAG_RESERVED_7 = 7 ,
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 ,
OBJC_TAG_FirstUnobfuscatedSplitTag = 136 ,
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/wp-content/uploads/2022/06/260dd503b20e99f07a96aecb0f2f0fc6.p变量之间的关系 ng”>
源码1688“https://www.6hu.cc/wp-content/uploads/2022/06/2f56a2c变量类型有哪些 59e6bd1c7bee71b8ab4cddd60.png”>
可以看出,解析出来的tag就对应着它们的类型,看一下解析tag的方法:
static inline objc_tag_index_t
_objc_getTaggedPointerTag(const void * _Nullable 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 特点的介绍:
Tagg字符串逆序输出 ed Pointer 专门用来存储小的对象,例如 NSNumber 和 NS字符间距 Date。
Tagged Pointer 指针的值不再是地址了,而是真正的值。所以,实际上它不再是一个对象
了,它只是一个披着对象“皮”的普通变量而己。所以,它的内存并不存储在堆中,也
不需要 malloc 和free。
3. 在内存读取上有着以前3倍的效率,创建时比以前快 106倍
因此,苹果引入 Tagged Pointer,字符间距在哪里设置 不但减少了 64位机器下程序的内存占用,还提高了运行效率,完美地解决了小内存对象在存储和访问效率上的问题。
注意事项
isa指针
Tagged Pointer 的引入也带来了问题字符间距在哪里设置 ,即 Tagged Pointer 并不是真正的对象,而是一个伪对象,所多线程的应用场景 以你如果完全把它当成对象来使用,可能会让它“露马脚”。在上一章中我们写道,所有对象都有isa 指针,而 Tagged Pointer 其实是没有的,多线程是什么 因为它不是真正的对象。
64位下的isa指针优化
对于 64位设备,苹果除了引人 Tagg效率符号 ed Pointer 来优化小的对象外,对于普通的对象,其多线程的实现方式 isa指针也进行了优化和调整。在32 位环境下,对象的引用计数都保存在一个外部的表中,每一个变量之间的关系 对象的 Retain 操作,实际上包括如下 5个步骤:
获得全局的记录引用计数的hash 表。
为了线程安全,给该hash 表加锁。
查找到目标对效率公式 象的引用计数值。
将该引用计数值加1,写回hash 表。
给该hash 表解锁。
从上面的步骤我们可以看出,为了保证源码交易平台 线程安全,对引用计数的增减操作都要先锁定这个表,这从性能上看变量类型有哪些 是非常差的。
而在 64 位环境下,isa 指针也是64 位,实际作为指针部分只用到的其中33位,剩余的 31位苹果使用了类似 Tagged Poin多线程并发中线程的状态 ter 的概念,其中 19 位将保存对象的引1用计数,这样对引用计数的操作只需要修改源码 这个指针即可。只有当引用计数超出19位,才会将引用计数保存到外部表,而这种情况是很少的,所以这样引用计数的更改效率会更高。
与前面的5个步骤对应,在64位字符间距怎么加宽 环境下,新的 Ret多线程是什么 ain 操作包括如下 5个步骤:
检查 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 pointer,Ta变量名的命名规则 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位机器中,虽然逻辑没有任何变化,但这种 NSNumber、 NSDate一类的对象所占用的内存会翻倍。
多线程并发中线程的状态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 对象。由于 NSNumber、NSDate一类的变量本身的值需要占源码编辑器 用的内存大小常常不需要8个字节,拿整数来说,4个字节所能表示的有符号整数就可以达变量泵 到20 多亿(注:2^31^ = 2147483648,另外1位作为符号位),对于绝大多数情况都是可以处理的。
所以我们可以将一个对象的指字符型变量 针拆成两部分,一部分直接保存数据,另一部分作为特殊标记,表示这源码编辑器下载 是一个特别的指针,不指向任何一个源码精灵永久兑换码 地址。所字符串是什么意思 以,引入了 Tagged Point变量值 er 对象之后,64位 CPU 下 NSNumber 的内存图变成了字符型变量 下面这样:
多线程应用场景例子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" );
运行结果:崩溃(坏内存访问)
原因分析:
因为setter方法中,对strong修饰的属性会有一个retain和release的操作。在并发多线程的赋值操作中,都是对_name指针进行的操作,可能在_name刚刚被release后进行赋值操作,这个时候_name指向的内存地址是已经被释放了,所以造成了坏内存访问崩溃。
- (void )setName:(NSString *)name {
[name retain ];
[_name relase];
_name = name;
}
解决办法:
将并发执效率计算公式 行的任务改为串行执行
dispatch_queue_t queue = dispatch_queue_create("queue" , DISPATCH_QUEUE_SERIAL);
将异步执行改为同步执行
dispatch_sync (queue, ^{
self .name = [NSString stringWithFormat:@"abcdefghijklmn" ];
});
将属性改为atomic属性原子性
加锁
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 代表有效负载,源码中有这样的注释:
多线程应用场景例子rc=”https://www.6hu.cc/wp-content/uploads/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)) {
arc4random_buf(&objc_debug_taggedpointer_obfuscator,
sizeof (objc_debug_taggedpointer_obfuscator));
objc_debug_taggedpointer_obfuscator &= ~_OBJC_TAG_MASK;
#if OBJC_SPLIT_TAGGED_POINTERS
objc_debug_taggedpointer_obfuscator &= ~(_OBJC_TAG_EXT_MASK | _OBJC_TAG_NO_OBFUSCATION_MASK);
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的值和代表的类型:
{
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 ,
OBJC_TAG_RESERVED_7 = 7 ,
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 ,
OBJC_TAG_FirstUnobfuscatedSplitTag = 136 ,
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/wp-content/uploads/2022/06/260dd503b20e99f07a96aecb0f2f0fc6.p变量之间的关系 ng”>
源码1688“https://www.6hu.cc/wp-content/uploads/2022/06/2f56a2c变量类型有哪些 59e6bd1c7bee71b8ab4cddd60.png”>
可以看出,解析出来的tag就对应着它们的类型,看一下解析tag的方法:
static inline objc_tag_index_t
_objc_getTaggedPointerTag(const void * _Nullable 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 特点的介绍:
Tagg字符串逆序输出 ed Pointer 专门用来存储小的对象,例如 NSNumber 和 NS字符间距 Date。
Tagged Pointer 指针的值不再是地址了,而是真正的值。所以,实际上它不再是一个对象
了,它只是一个披着对象“皮”的普通变量而己。所以,它的内存并不存储在堆中,也
不需要 malloc 和free。
3. 在内存读取上有着以前3倍的效率,创建时比以前快 106倍
因此,苹果引入 Tagged Pointer,字符间距在哪里设置 不但减少了 64位机器下程序的内存占用,还提高了运行效率,完美地解决了小内存对象在存储和访问效率上的问题。
注意事项
isa指针
Tagged Pointer 的引入也带来了问题字符间距在哪里设置 ,即 Tagged Pointer 并不是真正的对象,而是一个伪对象,所多线程的应用场景 以你如果完全把它当成对象来使用,可能会让它“露马脚”。在上一章中我们写道,所有对象都有isa 指针,而 Tagged Pointer 其实是没有的,多线程是什么 因为它不是真正的对象。
64位下的isa指针优化
对于 64位设备,苹果除了引人 Tagg效率符号 ed Pointer 来优化小的对象外,对于普通的对象,其多线程的实现方式 isa指针也进行了优化和调整。在32 位环境下,对象的引用计数都保存在一个外部的表中,每一个变量之间的关系 对象的 Retain 操作,实际上包括如下 5个步骤:
获得全局的记录引用计数的hash 表。
为了线程安全,给该hash 表加锁。
查找到目标对效率公式 象的引用计数值。
将该引用计数值加1,写回hash 表。
给该hash 表解锁。
从上面的步骤我们可以看出,为了保证源码交易平台 线程安全,对引用计数的增减操作都要先锁定这个表,这从性能上看变量类型有哪些 是非常差的。
而在 64 位环境下,isa 指针也是64 位,实际作为指针部分只用到的其中33位,剩余的 31位苹果使用了类似 Tagged Poin多线程并发中线程的状态 ter 的概念,其中 19 位将保存对象的引1用计数,这样对引用计数的操作只需要修改源码 这个指针即可。只有当引用计数超出19位,才会将引用计数保存到外部表,而这种情况是很少的,所以这样引用计数的更改效率会更高。
与前面的5个步骤对应,在64位字符间距怎么加宽 环境下,新的 Ret多线程是什么 ain 操作包括如下 5个步骤:
检查 isa 指针上面的标记位,看引1用计数是否保存在 isa变量中,如果不是,则使用以前
的步骤,否则执行第2步
2. 检查当前对象是源码编辑器下载 否正在释放,如果是,则不做任何事情。
3. 增加该对象的变量类型有哪些 引用计数,但是并不马上写回到isa 变量中。
4. 检杳增加后的引用计数的值是否能够被 19 位表示,如果不是,则切换成以前的办法,否
则执行第5步。
5. 进行一个原子的写操作,源码之家 将isa 的值写回。
虽然步骤都是 5步,但是由于没有了全局的加锁操作,所以引用源码编程器 计数的更改更快了。
评论(0)