NSStrig的三种形式

NSString的三种形式

引入

__NSCFConstantString__NSCFStringNSTaggedPointerString 都是 NSString 的内部实现方式,用于表示不同类型的字符串对象。它们在内存中的存储和处理方式有所不同。

image-20240519145250352

运行结果

image-20240519145347306

__NSCFConstantString

__NSCFConstantStringNSString 的一种实现方式,用于表示不可变的字符串常量。它是在编译时创建的,存储在常量区(const section)中,并且在程序生命周期内是不可修改的。由于是常量,多个相同的字符串常量在内存中只会存储一份,以节省内存空间。

  • 它是编译期间的常量字符串,存储在只读的数据段中,因此具有固定的内存地址。
  • 这种字符串类型通常用于静态字符串的表示,比如使用 @"Hello" 这样的字符串字面量。

从他的引用计数来看,是一个很大的数,所以可以理解为是不可以被释放的,而且只在内存之中存储一份,所以我们可以理解为其为一种单例的形式

对于__NSCFConstantString创建我们可以使用@" "或者stringWithString跟一个常量字符串来进行创建。

__NSCFString

__NSCFStringNSString 的另一种实现方式,用于表示不可变的字符串对象。它是基于动态分配的内存,可以包含任意长度的字符序列。当你使用字符串字面量或调用 initWithFormat:stringWithFormat: 等方法创建不可变字符串时,会生成一个 __NSCFString 对象。在创建的时候,会获得一次引用计数,说明该对象是可以被释放的。

str1以及str1_的结果我们可以看到,这种类型的字符串被创建在堆上,即使内容相同的字符串也会重新为其创建对象,用不同的指针指向的也是不同的对象

NSTaggedPointerString

标记指针(Tagged Pointer)是一种优化技术,通过提高空间利用率和访问效率来改善程序的性能和效率。用于在不分配额外内存的情况下存储小的对象或数字值。这是苹果在 64 位环境下对 NSString,NSNumber等对象做的一些优化。**简单来讲当对象的大小小于一个指针的大小时,可以直接将数据存储在指针中,而不需要额外的内存分配。**因为在 64 位环境下,OC的相关变量指针变量的大小达到了 8位,这个大小足以容纳一些长度较小的内容。于是就使用了标签指针这种方式来优化数据的存储方式。

当数字、英文字母字符串的长度小于等于 9 且不包含中文字符或其他特殊符号时,它们会自动成为 NSTaggedPointerString 类型,以节省内存并提高访问速度。而一旦字符串中包含中文字符或其他特殊符号,它们将被存储为 __NSCFString 类型,需要额外的内存分配。这种方式避免了内存分配和释放的开销,提高了程序的性能和内存利用率。

三种字符串的复制情况

为了测试以上三种字符串使用copymutableCopy的结果,我们使用以下程序

#import <Foundation/Foundation.h>

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        NSString *str1 = @"Hello";
        NSString *str2 = [NSString stringWithFormat:@"Hello,world"];
        NSString *str3= [NSString stringWithFormat:@"World"];
        // copy
        NSString *str1ShallowCopy = [str1 copy];
        NSString *str2ShallowCopy = [str2 copy];
        NSString *str3ShallowCopy = [str3 copy];
        //使用stringWithString方法
        NSString *str1_ = [NSString stringWithString:str1];
        NSString *str2_ = [NSString stringWithString:str2];
        NSString *str3_ = [NSString stringWithString:str3];
        // mutablecopy
        NSMutableString *str1MutableDeepCopy = [str1 mutableCopy];
        NSMutableString *str2MutableDeepCopy = [str2 mutableCopy];
        NSMutableString *str3MutableDeepCopy = [str3 mutableCopy];
        
        NSLog(@"原始字符串:");
        NSLog(@"str1 = %p, %@, %lu", str1, [str1 class], [str1 retainCount]);
        NSLog(@"str2 = %p, %@, %lu", str2, [str2 class], [str2 retainCount]);
        NSLog(@"str3 = %p, %@, %lu", str3, [str3 class], [str3 retainCount]);
        
        NSLog(@"浅拷贝:");
        NSLog(@"str1ShallowCopy = %p, %@, %lu", str1ShallowCopy, [str1ShallowCopy class], [str1ShallowCopy retainCount]);
        NSLog(@"str2ShallowCopy = %p, %@, %lu", str2ShallowCopy, [str2ShallowCopy class], [str2ShallowCopy retainCount]);
        NSLog(@"str3ShallowCopy = %p, %@, %lu", str3ShallowCopy, [str3ShallowCopy class], [str3ShallowCopy retainCount]);
        
        NSLog(@"stringWithString:");
        NSLog(@"str1_ = %p, %@, %lu", str1_, [str1_ class], [str1_ retainCount]);
        NSLog(@"str2_ = %p, %@, %lu", str2_, [str2_ class], [str2_ retainCount]);
        NSLog(@"str3_ = %p, %@, %lu", str3_, [str3_ class], [str3_ retainCount]);
        
        NSLog(@"深拷贝:");
        NSLog(@"str1MutableDeepCopy = %p, %@, %lu", str1MutableDeepCopy, [str1MutableDeepCopy class], [str1MutableDeepCopy retainCount]);
        NSLog(@"str2MutableDeepCopy = %p, %@, %lu", str2MutableDeepCopy, [str2MutableDeepCopy class], [str2MutableDeepCopy retainCount]);
        NSLog(@"str3MutableDeepCopy = %p, %@, %lu", str3MutableDeepCopy, [str3MutableDeepCopy class], [str3MutableDeepCopy retainCount]);
        return 0;
    }
}

以上程序我们可以得到以下结果:

我们可以看到copystringWithString得到的结果是一样的,通过这个结果,stringWithString在一定程度上是可以等价的。在我们使用copy时,NSString的类型不会进行改变,由于__NSCFConstantString,NSTaggedPointerString无法被释放的,引用计数不会发生变化,但对于__NSCFString来说就使得其计数器加一。

对于mutableCopy来说呢,当对于任何对象使用它,最后都只会得到__NSCFString类型的字符串,引用计数为1,其原因应该在于mutableCopy对以上三种字符串进行拷贝的时候,创建的都为可变类的容器,所以需要在堆区重新开辟空间进行相应的操作。

总结:

在 Objective-C 中,有三种常见的字符串实现方式:__NSCFConstantString__NSCFStringNSTaggedPointerString

  1. __NSCFConstantString 是不可变的字符串常量,它是在编译时创建的,存储在常量区,且在程序生命周期内不可修改。多个相同的字符串常量在内存中只会存储一份。

  2. __NSCFString 是不可变的字符串对象,基于动态分配的内存,可以包含任意长度的字符序列。当使用字符串字面量或调用相关的初始化方法创建不可变字符串时,会生成一个 __NSCFString 对象。

  3. NSTaggedPointerString 是一种优化方式,在64位环境下用于存储小的对象或数字值。它将数据直接存储在指针中,而不需要额外的内存分配。当字符串的长度小于等于9且只包含数字、英文字母等特定字符时,会自动成为 NSTaggedPointerString 对象。

在进行复制操作时,可以使用 copymutableCopy 方法。对于这三种字符串的复制操作:

  • copy 方法会创建一个不可变的副本,对于 __NSCFConstantStringNSTaggedPointerString 类型的字符串,复制后的对象依然是相同类型,引用计数不变。对于 __NSCFString 类型的字符串,复制后会创建一个新的对象,并且引用计数加一。

  • mutableCopy 方法会创建一个可变的副本,不论原始字符串是什么类型,最终得到的副本都是 __NSCFString 类型的字符串,引用计数为1。

需要注意的是,stringWithString: 方法在一定程度上可以等价于 copy 操作。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值