iOS NSString 內(nèi)存管理

iOS中,NSString對象相對其他OC對象的內(nèi)存管理是復雜的。
按照產(chǎn)生對象的isa,大致可以分為三種情況,如下:

1、__NSCFConstantString

字符串常量,是一種編譯時常量,retainCount值很大,對其操作,不會引起引用計數(shù)變化。
這種對象存儲在字符串常量區(qū)。

2、__NSCFString

__NSCFString對象是在運行時創(chuàng)建的一種NSString子類,創(chuàng)建后引用計數(shù)會加1。
這種對象存儲在堆上。

3、NSTaggedPointerString

標簽指針,是蘋果在64位環(huán)境下對NSString、NSNumber等對象做的一些優(yōu)化。

簡單來講,可以理解為把指針指向的內(nèi)容直接放在了指針變量的內(nèi)存地址中,因為在 64 位環(huán)境下指針變量的大小達到了 8 位足以容納一些長度較小的內(nèi)容。于是使用了標簽指針這種方式來優(yōu)化數(shù)據(jù)的存儲方式。在運行時根據(jù)實際情況創(chuàng)建。

對于 NSString 對象,當字符串是由數(shù)字、英文字母組合且長度小于等于 9 的時候會自動成為 NSTaggedPointerString 類型。如果有中文或其他特殊符號,則會直接成為 __NSCFString 類型。

代碼驗證

環(huán)境:Xcode 10.2 、iOS 12.2

一、數(shù)字和字母組合(長度小于9)
/**
 測試拷貝
 */
- (void)testCopyAndMutableCopy {
    [self testString];
    [self testStringCopy1];
    [self testStringCopy2];
    [self testStringCopy3];
    [self testMutableStringCopy1];
    [self testMutableStringCopy2];
}

/**
 測試不可變字符串
 */
- (void)testString {
    NSString *testStr00 = @"abc123";
    NSLog(@"testStr00:%p ** %@ ** %@", testStr00, [testStr00 class], testStr00);
    
    printf("\n");
}

/**
 測試不可變字符串
 */
- (void)testStringCopy1 {
    NSString *testStr11 = @"abc123";
    NSLog(@"testStr11:%p ** %@ ** %@", testStr11, [testStr11 class], testStr11);
    
    id testStr21 = [testStr11 copy];
    NSLog(@"testStr21:%p ** %@ ** %@", testStr21, [testStr21 class], testStr21);
    
    id testStr31 = [testStr11 mutableCopy];
    NSLog(@"testStr31:%p ** %@ ** %@", testStr31, [testStr31 class], testStr31);
    
    printf("\n");
}

/**
 測試不可變字符串
 */
- (void)testStringCopy2 {
    NSString *testStr12 = [NSString stringWithFormat:@"abc123"];
    NSLog(@"testStr12:%p ** %@ ** %@", testStr12, [testStr12 class], testStr12);
    
    id testStr22 = [testStr12 copy];
    NSLog(@"testStr22:%p ** %@ ** %@", testStr22, [testStr22 class], testStr22);
    
    id testStr32 = [testStr12 mutableCopy];
    NSLog(@"testStr32:%p ** %@ ** %@", testStr32, [testStr32 class], testStr32);
    
    printf("\n");
}

/**
 測試不可變字符串
 */
- (void)testStringCopy3 {
    NSString *testStr13 = [[NSString alloc] initWithFormat:@"abc123"];
    NSLog(@"testStr13:%p ** %@ ** %@", testStr13, [testStr13 class], testStr13);
    
    id testStr23 = [testStr13 copy];
    NSLog(@"testStr23:%p ** %@ ** %@", testStr23, [testStr23 class], testStr23);
    
    id testStr33 = [testStr13 mutableCopy];
    NSLog(@"testStr33:%p ** %@ ** %@", testStr33, [testStr33 class], testStr33);
    
    printf("\n");
}

/**
 測試可變字符串
 */
- (void)testMutableStringCopy1 {
    NSMutableString *testMuStr14 = [[NSMutableString alloc] initWithString:@"abc123"];
    NSLog(@"testMuStr14:%p ** %@ ** %@",testMuStr14, [testMuStr14 class], testMuStr14);
    
    id testMuStr24 = [testMuStr14 copy];
    NSLog(@"testMuStr24:%p ** %@ ** %@",testMuStr24, [testMuStr24 class], testMuStr24);
    
    id testMuStr34 = [testMuStr14 mutableCopy];
    NSLog(@"testMuStr34:%p ** %@ ** %@",testMuStr34, [testMuStr34 class], testMuStr34);
    
    printf("\n");
}

/**
 測試可變字符串
 */
- (void)testMutableStringCopy2 {
    NSMutableString *testMuStr15 = [[NSMutableString alloc] initWithFormat:@"abc123"];
    NSLog(@"testMuStr15:%p ** %@ ** %@",testMuStr15, [testMuStr15 class], testMuStr15);
    
    id testMuStr25 = [testMuStr15 copy];
    NSLog(@"testMuStr25:%p ** %@ ** %@",testMuStr25, [testMuStr25 class], testMuStr25);
    
    id testMuStr35 = [testMuStr15 mutableCopy];
    NSLog(@"testMuStr35:%p ** %@ ** %@",testMuStr35, [testMuStr35 class], testMuStr35);
    
    printf("\n");
}

測試結(jié)果:

testStr00:0x1063cc0a0 ** __NSCFConstantString ** abc123

testStr11:0x1063cc0a0 ** __NSCFConstantString ** abc123
testStr21:0x1063cc0a0 ** __NSCFConstantString ** abc123
testStr31:0x600001bc65b0 ** __NSCFString ** abc123

testStr12:0x91472c787c5ba8b1 ** NSTaggedPointerString ** abc123
testStr22:0x91472c787c5ba8b1 ** NSTaggedPointerString ** abc123
testStr32:0x600001b8b540 ** __NSCFString ** abc123

testStr13:0x91472c787c5ba8b1 ** NSTaggedPointerString ** abc123
testStr23:0x91472c787c5ba8b1 ** NSTaggedPointerString ** abc123
testStr33:0x600001bc65b0 ** __NSCFString ** abc123

testMuStr14:0x600001b8b540 ** __NSCFString ** abc123
testMuStr24:0x91472c787c5ba8b1 ** NSTaggedPointerString ** abc123
testMuStr34:0x600001bc65b0 ** __NSCFString ** abc123

testMuStr15:0x600001b913e0 ** __NSCFString ** abc123
testMuStr25:0x91472c787c5ba8b1 ** NSTaggedPointerString ** abc123
testMuStr35:0x600001b90ed0 ** __NSCFString ** abc123

從上面測試結(jié)果來看,有以下結(jié)論:

1、testStr00testStr11testStr21具有相同的內(nèi)存地址(0x1063cc0a0)。對__NSCFConstantString對象進行copy操作,不引起引用計數(shù)變化;進行mutableCopy操作,會拷貝對象,生成新的__NSCFString對象(內(nèi)存地址會發(fā)生變化)。

2、testStr12testStr22、testStr13、testStr12具有相同的內(nèi)存地址(0x91472c787c5ba8b1)。對NSTaggedPointerString對象進行copy操作,不引起引用計數(shù)變化;進行mutableCopy操作,會拷貝對象,生成新的__NSCFString對象(內(nèi)存地址會發(fā)生變化)。

3、比較有意思的是testStr31testStr33具有相同的內(nèi)存地址(0x600001bc65b0),但是與testStr32是不一樣的(0x600001b8b540)。推測多次對同一字符串常量進行mutableCopy操作,生成新的__NSCFString對象,有可能同一對象、也有可能不是同一對象。

4、對__NSCFString對象進行copy操作,可能會生成NSTaggedPointerString對象;也可能會生成__NSCFString對象。(參照下面帶中文的字符串測試結(jié)果)
進行mutableCopy操作,會拷貝對象,生成新的__NSCFString對象(內(nèi)存地址會發(fā)生變化)。

二、帶中文組合
/**
 測試拷貝
 */
- (void)testCopyAndMutableCopy {
    [self testString];
    [self testStringCopy1];
    [self testStringCopy2];
    [self testStringCopy3];
    [self testMutableStringCopy1];
    [self testMutableStringCopy2];
}

/**
 測試不可變字符串
 */
- (void)testString {
    NSString *testStr00 = @"你好abc";
    NSLog(@"testStr00:%p ** %@ ** %@", testStr00, [testStr00 class], testStr00);
    
    printf("\n");
}

/**
 測試不可變字符串
 */
- (void)testStringCopy1 {
    NSString *testStr11 = @"你好abc";
    NSLog(@"testStr11:%p ** %@ ** %@", testStr11, [testStr11 class], testStr11);
    
    id testStr21 = [testStr11 copy];
    NSLog(@"testStr21:%p ** %@ ** %@", testStr21, [testStr21 class], testStr21);
    
    id testStr31 = [testStr11 mutableCopy];
    NSLog(@"testStr31:%p ** %@ ** %@", testStr31, [testStr31 class], testStr31);
    
    printf("\n");
}

/**
 測試不可變字符串
 */
- (void)testStringCopy2 {
    NSString *testStr12 = [NSString stringWithFormat:@"你好abc"];
    NSLog(@"testStr12:%p ** %@ ** %@", testStr12, [testStr12 class], testStr12);
    
    id testStr22 = [testStr12 copy];
    NSLog(@"testStr22:%p ** %@ ** %@", testStr22, [testStr22 class], testStr22);
    
    id testStr32 = [testStr12 mutableCopy];
    NSLog(@"testStr32:%p ** %@ ** %@", testStr32, [testStr32 class], testStr32);
    
    printf("\n");
}

/**
 測試不可變字符串
 */
- (void)testStringCopy3 {
    NSString *testStr13 = [[NSString alloc] initWithFormat:@"你好abc"];
    NSLog(@"testStr13:%p ** %@ ** %@", testStr13, [testStr13 class], testStr13);
    
    id testStr23 = [testStr13 copy];
    NSLog(@"testStr23:%p ** %@ ** %@", testStr23, [testStr23 class], testStr23);
    
    id testStr33 = [testStr13 mutableCopy];
    NSLog(@"testStr33:%p ** %@ ** %@", testStr33, [testStr33 class], testStr33);
    
    printf("\n");
}

/**
 測試可變字符串
 */
- (void)testMutableStringCopy1 {
    NSMutableString *testMuStr14 = [[NSMutableString alloc] initWithString:@"你好abc"];
    NSLog(@"testMuStr14:%p ** %@ ** %@",testMuStr14, [testMuStr14 class], testMuStr14);
    
    id testMuStr24 = [testMuStr14 copy];
    NSLog(@"testMuStr24:%p ** %@ ** %@",testMuStr24, [testMuStr24 class], testMuStr24);
    
    id testMuStr34 = [testMuStr14 mutableCopy];
    NSLog(@"testMuStr34:%p ** %@ ** %@",testMuStr34, [testMuStr34 class], testMuStr34);
    
    printf("\n");
}

/**
 測試可變字符串
 */
- (void)testMutableStringCopy2 {
    NSMutableString *testMuStr15 = [[NSMutableString alloc] initWithFormat:@"你好abc"];
    NSLog(@"testMuStr15:%p ** %@ ** %@",testMuStr15, [testMuStr15 class], testMuStr15);
    
    id testMuStr25 = [testMuStr15 copy];
    NSLog(@"testMuStr25:%p ** %@ ** %@",testMuStr25, [testMuStr25 class], testMuStr25);
    
    id testMuStr35 = [testMuStr15 mutableCopy];
    NSLog(@"testMuStr35:%p ** %@ ** %@",testMuStr35, [testMuStr35 class], testMuStr35);
    
    printf("\n");
}

測試結(jié)果:

testStr00:0x108a610a0 ** __NSCFConstantString ** 你好abc

testStr11:0x108a610a0 ** __NSCFConstantString ** 你好abc
testStr21:0x108a610a0 ** __NSCFConstantString ** 你好abc
testStr31:0x6000007f6e50 ** __NSCFString ** 你好abc

testStr12:0x6000007f6d60 ** __NSCFString ** 你好abc
testStr22:0x6000007f6d60 ** __NSCFString ** 你好abc
testStr32:0x600000790bd0 ** __NSCFString ** 你好abc

testStr13:0x6000007f6d30 ** __NSCFString ** 你好abc
testStr23:0x6000007f6d30 ** __NSCFString ** 你好abc
testStr33:0x600000790bd0 ** __NSCFString ** 你好abc

testMuStr14:0x6000007c9350 ** __NSCFString ** 你好abc
testMuStr24:0x6000007f6d30 ** __NSCFString ** 你好abc
testMuStr34:0x600000790bd0 ** __NSCFString ** 你好abc

testMuStr15:0x6000007c9350 ** __NSCFString ** 你好abc
testMuStr25:0x6000007c9650 ** __NSCFString ** 你好abc
testMuStr35:0x600000790bd0 ** __NSCFString ** 你好abc
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容