> For the complete documentation index, see [llms.txt](https://ryukiedev.gitbook.io/wiki/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://ryukiedev.gitbook.io/wiki/ios/nei-cun-guan-li/01.-cong-yi-ge-mian-shi-ti-kan-taggedpointer.md).

# 01.从一个面试题看TaggedPointer

### 前言

之前我们在[WWDC20-runtime优化](/wiki/ios/di-ceng/06.wwdc20runtime-you-hua.md)中有初步接触到了 `TaggedPointer` 。本文就讲结合一个面试题加深对它的理解。

### 一、 一个面试题

分析一下下面的代码会有什么问题

```
#import "ViewController.h"

@interface ViewController ()

@property (nonatomic, strong) dispatch_queue_t  queue;
@property (nonatomic, strong) NSString *nameStr;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    [self taggedPointerDemo];

}

- (void)taggedPointerDemo {
  
    self.queue = dispatch_queue_create("Ryukie", DISPATCH_QUEUE_CONCURRENT);
    
    for (int i = 0; i<100000; i++) {
        dispatch_async(self.queue, ^{
            self.nameStr = [NSString stringWithFormat:@"Ryukie"];
             NSLog(@"%@",self.nameStr);
        });
    }
}

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    for (int i = 0; i<100000; i++) {
        dispatch_async(self.queue, ^{
            self.nameStr = [NSString stringWithFormat:@"RyukieRyukieRyukieRyukieRyukieRyukieRyukieRyukieRyukieRyukie"];
            NSLog(@"%@",self.nameStr);
        });
    }
}

@end
```

#### 1.1 分析

乍一看本题考验的是多线程读写，很难想到和 `TaggedPointer` 有关。本题两处中的不同点是字符串的长度而已。而根据我们之前对 `TaggedPointer` 的了解，其中的 `Payload` 会用来存储数据。但是 `Payload` 的长度是有限的，过长的数据就无法完整保存进去。

所以，这里分析两处的指针类型是不同的。

#### 1.2 断点调试

**较短的字符串**

![1](/files/tQAup0JWuYf1ICdIJyf8)

这里发现指针类型为 `NSTaggedPointerString` 。明显是一个 `TaggedPointer` 。

**较长的字符串**

![2](/files/6EHuPl1ifwkM6XurMVtT)

这里的指针类型为 `__NSCFString` 是一个普通的指针。

#### 1.3 问题

![3](/files/lcU2ISZWeEnPBMGMX2ls)

在第二段代码运行中出现了野指针的崩溃。

#### 1.4 思考

根据奔溃的现象，你可能会想到。 `TaggedPointer` 的 `Retain` 和 `Release` 难道和普通的有不同么？

### 二、 源码分析

在 OC 源码中找到：

```
- (id)retain {
    return _objc_rootRetain(self);
}
```

进一步找到具体实现：

```
objc_object::rootRetain(bool tryRetain, objc_object::RRVariant variant)
{
    if (slowpath(isTaggedPointer())) return (id)this;
    ...
}
```

通过 `Retain` 的源码，我们可以看出，在判断是 `TaggedPointer` 之后就直接 `return` 了，没有进行任何操作。

那么 Release 呢？我们探索一下源码：

```
objc_object::rootRelease(bool performDealloc, objc_object::RRVariant variant)
{
    if (slowpath(isTaggedPointer())) return false;
    ...
}
```

发现这里和 `Retain` 是一样的处理。

### 三、 问题分析

初步了解了 Retain 和 Release 对与 `TaggedPointer` 之后，我们就很容易理解上面的面试题所发生的现象了

* `TaggedPointer` 的较短的字符串没有进行 Retain Release 所以多线程异步读写过程中没有出现野指针错误
* 非 `TaggedPointer` 的较长的字符串，由于进行了多线程异步读写操作，不断的 Retain Release ，所以可能出现野指针错误。

### 总结

* `TaggedPointer` 是用于特定类型的小对象的，并不是那些类型就都是 `TaggedPointer` 。前提是 `Payload` 要能够装得下。
* `Retain` `Release` 对 `TaggedPointer` 的特殊处理，也提高了 `TaggedPointer` 的效率，同时在多线程场景下的安全性也比较高。


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://ryukiedev.gitbook.io/wiki/ios/nei-cun-guan-li/01.-cong-yi-ge-mian-shi-ti-kan-taggedpointer.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
