iOS - Tagged Pointer
1. 基本结构
// Tagged Pointer 的内存布局
union TaggedPointer {
uintptr_t bits; // 完整的指针值
struct {
uintptr_t data : 60; // 数据部分
uintptr_t tag : 4; // 类型标记
};
// 扩展类型
struct {
uintptr_t extData : 52; // 扩展数据
uintptr_t extTag : 8; // 扩展标记
uintptr_t isExt : 4; // 是否是扩展类型
};
};
2. 类型判断
// 判断是否是 Tagged Pointer
static inline bool
isTaggedPointer(const void *ptr) {
return ((uintptr_t)ptr & _OBJC_TAG_MASK) == _OBJC_TAG_MASK;
}
// 获取类型标记
static inline uintptr_t
getTaggedPointerTag(const void *ptr) {
return ((uintptr_t)ptr >> _OBJC_TAG_SLOT_SHIFT) & _OBJC_TAG_MASK;
}
3. 支持的数据类型
3.1 基本类型
// NSNumber 的 Tagged Pointer 实现
+ (NSNumber *)numberWithInt:(int)value {
// 如果值在 Tagged Pointer 范围内
if (value >= MIN_TAGGED_INT && value <= MAX_TAGGED_INT) {
uintptr_t tagged = ((uintptr_t)value << 4) | NSNumberTag;
return (__bridge id)(void *)tagged;
}
// 否则创建普通对象
return [[NSNumber alloc] initWithInt:value];
}
3.2 字符串类型
// NSString 的 Tagged Pointer 实现
+ (NSString *)stringWithSmallString:(const char *)str {
size_t length = strlen(str);
if (length <= 7) { // 可以用 Tagged Pointer 存储
uintptr_t tagged = 0;
for (size_t i = 0; i < length; i++) {
tagged |= ((uintptr_t)str[i] << (i * 8));
}
tagged |= (NSStringTag << 60); // 添加标记
return (__bridge id)(void *)tagged;
}
return [[NSString alloc] initWithUTF8String:str];
}
4. 内存管理
4.1 引用计数
// Tagged Pointer 不需要引用计数
id objc_retain(id obj) {
if (isTaggedPointer(obj)) return obj;
return obj->retain();
}
void objc_release(id obj) {
if (isTaggedPointer(obj)) return;
return obj->release();
}
4.2 内存优化
// 直接存储数据,无需额外内存分配
+ (instancetype)optimizedNumberWithInt:(int)value {
if (canBeTaggedPointer(value)) {
return makeTaggedPointer(value);
}
return [self newNumberWithInt:value];
}
5. 消息发送
id objc_msgSend_tagged(id self, SEL _cmd, ...) {
// 1. 获取类型标记
uintptr_t tag = getTaggedPointerTag(self);
// 2. 获取对应的类
Class cls = objc_getTaggedPointerClass(tag);
// 3. 查找方法
IMP imp = lookUpImpOrForward(cls, _cmd);
// 4. 调用方法
return imp(self, _cmd, ...);
}
6. 性能优化
6.1 空间优化
// 普通对象
struct NSNumber {
Class isa;
int value; // 至少需要 16 字节
};
// Tagged Pointer
// 直接使用指针存储值,只需要 8 字节
uintptr_t taggedNumber = (value << 4) | tag;
6.2 时间优化
// 普通对象访问
int getValue(NSNumber *num) {
// 1. 解引用 isa
// 2. 解引用获取值
return num->_value;
}
// Tagged Pointer 访问
int getTaggedValue(NSNumber *num) {
// 直接从指针中提取值
return (uintptr_t)num >> 4;
}
7. 限制和注意事项
7.1 大小限制
// 数据大小限制
#define TAGGED_POINTER_MASK 0xF000000000000000
#define TAGGED_POINTER_DATA 0x0FFFFFFFFFFFFFFF
bool canBeTagged(size_t size) {
// 60位数据位的限制
return size <= (sizeof(uintptr_t) * 8 - 4);
}
7.2 类型限制
// Tagged Pointer 支持的类型定义
static Class taggedPointerClasses[] = {
[OBJC_TAG_NSAtom] = NSAtom, // 0
[OBJC_TAG_NSString] = NSString, // 1
[OBJC_TAG_NSNumber] = NSNumber, // 2
[OBJC_TAG_NSIndexPath] = NSIndexPath, // 3
[OBJC_TAG_NSManagedObjectID] = NSManagedObjectID, // 4
[OBJC_TAG_NSDate] = NSDate, // 5
[OBJC_TAG_NSDateTS] = NSDateTS, // 6
[OBJC_TAG_NSDecimalNumber] = NSDecimalNumber // 7
};
8. 实际应用
8.1 字符串优化
NSString *str1 = @"abc"; // 可能使用 Tagged Pointer
NSString *str2 = @"这是一个很长的字符串"; // 普通对象
Tagged Pointer 的优势:
- 减少内存分配
- 提高访问速度
- 减少引用计数操作
- 优化内存使用
这种优化对于小对象的处理特别有效,是一个重要的性能优化手段。