本文介绍: 最近被学长问到了有关NSString底层三种实现方式,但是回答不上来,特此撰写博客记录本文讲述了NSString三种实现方式,接下来总结一下学到的知识我们一般通过@“…”、CFSTR(“…”)或者方法生成这种类型这种对象存储常量区,是一种编译常量,同时还是一种单例好处是可以提高字符串访问效率,并且节省内存空间通过 NSString 的等方法创建的 NSString 对象一般都是这种类型。他并不是一种字符串常量。所以和其他的对象一样在被创建时获得了 1 的引用计数

提示文章写完后,目录可以自动生成如何生成参考右边的帮助文档


前言

最近被学长问到了有关NSString底层三种实现方式,但是回答不上来,特此撰写博客记录


实现方式

在 OC中,NSString类型有三种实现方式,分别是

__NSCFConstantString
__NSCFString
NSTaggedPointerString

知识引入

在这里插入图片描述

编译结果
在这里插入图片描述

这里有一点需要注意的就是我们使用引用计数时,我们必须要先关闭我们的ARC
在这里插入图片描述

##理解实现方式

可以看到我们用不同方式创建不同长度字符串出现了不同的类型,接下来我们一一对其进行解释

__NSCFConstantString

我们可以对这个类型的通俗理解就是常量字符串
在OC中,字符串字面量(例如@“Hello”)被视为常量,并存储在常量数据区域。它被看作是一种编译时常量。它用于表示字符字面量,这些字符串在编译时就被确定,并在运行时作为常量存储在常量数据区域
编译器会将字符串字面优化为常量,在编译时就确定其内容,并将其存储可执行文件的常量数据区域
这样做的好处是可以提高字符串的访问效率,并且节省内存空间因为每个相同的字符串字面量只需要存储一份,这就对应着我们str1与str1_的地址是相等的。

同时我们通过相同方式创建两个对象如果地址相等,那么也证明他是一种单例

另外我们可以看到它的引用计数十分庞大,说明这种类型的实例并不能被释放

所以在这里我们得出结论,常量字符串是一种单例
在这里插入图片描述

__NSCFConstantString 对象的特点是不可变的,即无法修改内容。由于字符串字面量是不可变的,因此它们在创建后不能被修改。__NSCFConstantString
类型的对象被设计只读,以确保字符串字面量的不可变性。

我们一般通过@“…”、CFSTR(“…”) 或者 stringWithString: 方法来生成这种类型

这种对象存储在字符串常量区

__NSCFString

和 __NSCFConstantString 不同, __NSCFString 对象是在运行创建的一种 NSString 子类,他并不是一种字符串常量。所以和其他的对象一样在被创建时获得了 1 的引用计数

通过 NSString 的 stringWithFormat 等方法创建的 NSString 对象一般都是这种类型。

这种对象被存储在堆上。

在我们的代码可以看到
在这里插入图片描述

我们将相同类型的字符串通过stringWithFormat方法赋给不同的实例时,实例地址不同,也间接说明了存储在堆上的对象,即使两个对象的内容相同,它们在堆上的内存地址也是不同的。每个对象都在独立内存空间中存储,具有自己地址。这意味着通过不同的对象引用访问这两个对象时,实际上访问的是不同的内存地址

NSTaggedPointerString

NSTaggedPointerString 是一种特殊类型的字符串,在 Objective-C 中用于表示较短的字符串对象。

TaggedPointer的意思是标签指针,这是苹果在 64 位环境下对 NSString,NSNumber
等对象做的一些优化简单来讲可以理解为把指针指向内容直接放在了指针变量内存地址,因为在 64 位环境指针变量大小达到了 8
位足以容纳一些长度较小的内容。于是使用标签指针这种方式来优化数据的存储方式。从他的引用计数可以看出,这货也是一个释放不掉的单例常量对象。在运行时根据实际情况创建

对于 NSString 对象来讲,当非字面值常量的数字英文字母字符串的长度小于等于 9 的时候自动成为 NSTaggedPointerString 类型,如果有中文或其他特殊符号(可能是非 ASCII 字符)存在的话则会直接成为 )__NSCFString 类型。

这一点在代码实例中也有体现

这种对象被直接存储在指针内容中,可以当作一种伪对象。

三种类型的深浅复制

我们先回顾一下我们深浅复制定义

浅复制是指创建一个新对象,该对象与原始对象共享内部数据引用。换句话说,浅复制只复制对象本身,而不复制对象所引用数据。因此,原始对象和副本对象指向同一块内存,对其中一个对象的修改影响另一个对象。
通俗的讲,浅复制就是仅仅创建一个存放与复制对象相同地址的指针变量也可以称为指针拷贝

深复制是指创建一个新对象,并复制原始对象及其引用的所有数据。深复制会递归地复制对象的所有内容,包括对象所引用的其他对象。这样,原始对象和副本对象拥有独立内存空间,彼此之间的修改互不影响
通俗的讲,深复制就是创建一个与被复制的对象的值完全相同的对象,但是他们的地址是不同的,因此深复制也可以称为内容拷贝

拷贝就是拷贝指向原来对象的指针,使原对象的引用计数+1,可以理解为创建了一个指向原对象的新指针而已,并没有创建一个全新的对象。而深复制则不会是引用计数+1.

在这里插入图片描述

编译结果
在这里插入图片描述

通过编译结果可以看到,我们的NSString的三种实现方式的深浅复制与我们容器类对象的深浅复制完全相同。至于容器类的深浅复制的原理,笔者会另外撰写一篇博客来专门讲述。

这里我们还可以明白copy被称为指针拷贝的原因,当我们对我们的__NSCFString类的对象进行浅拷贝时,可以看到它的引用计数加了1。同时因为其他两种类时单例,所以无论怎么进行操作其引用计数都不会增加

这里还有一点需要注意的,就是我们对NSString类进行复制时,复制后的对象全部都属于__NSCFConstantString。

这一点笔者通过使用ChatGPT解决了疑问,但并不能保证其正确性,在后续学习过程中如果有误会返回进行修改
在这里插入图片描述

总结

本文讲述了NSString的三种实现方式,接下来总结一下学到的知识

1、__NSCFConstantString:

我们一般通过@“…”、CFSTR(“…”) 或者 stringWithString: 方法来生成这种类型

这种对象存储在常量区,是一种编译时常量,同时还是一种单例

好处是可以提高字符串的访问效率,并且节省内存空间

2、__NSCFString

通过 NSString 的 stringWithFormat 等方法创建的 NSString 对象一般都是这种类型。

他并不是一种字符串常量。所以和其他的对象一样在被创建时获得了 1 的引用计数。

这种对象被存储在堆上。

3、NSTaggedPointerString

通过 NSString 的 stringWithFormat: 等方法创建的 NSString 对象有可能是这种类型,取决于字符串的长度

小于等于9的数字英文字母字符可能会成为这种类型,对于中文与其他特殊字符就直接成为__NSCFString类型

也是一个释放不掉的单例常量对象。在运行时根据实际情况创建。

这种对象被直接存储在指针的内容

4、三种类型的深浅复制

符合容器类对象的深浅复制原理

问题

1、为什么用@与stringWithString方法创建的对象是同一个对象:

stringWithString: 方法用于创建一个新的字符串对象,该对象是传入的字符串对象的副本。而字符串字面量(例如
@“Hello”)已经是一个不可变的 NSString 对象,不需要复制副本,因为它们本身就是不可变的。

2、我们为什么无法直接创建这些实例

在 Objective-C 中,由于 __NSCFConstantString、__NSCFString 和
NSTaggedPointerString 是运行时库的私有类,因此无法直接创建它们的实例。相反,我们可以通过使用 NSString 或
NSMutableString 类型的对象来间接地创建这些实现方式。

3、运行库的私有类是什么意思

在 Objective-C 中,运行时库的私有类是指在编译器官方文档中未公开或未公开文档化的类。这些类是运行时库的内部实现细节用于支持
Objective-C 的各种功能特性。它们通常不直接暴露开发者使用,而是由公共类进行封装和提供更高级接口

私有类在运行时库中扮演着重要的角色,用于实现字符串、数组集合内存管理消息传递核心功能。这些类可能包含更底层的数据结构算法,用于提高性能、优化内存占用和实现特定的语言特性

原文地址:https://blog.csdn.net/weixin_72437555/article/details/130769750

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任

如若转载,请注明出处:http://www.7code.cn/show_25472.html

如若内容造成侵权/违法违规/事实不符,请联系代码007邮箱suwngjj01@126.com进行投诉反馈,一经查实,立即删除

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注