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、testStr00與testStr11、testStr21具有相同的內(nèi)存地址(0x1063cc0a0)。對__NSCFConstantString對象進行copy操作,不引起引用計數(shù)變化;進行mutableCopy操作,會拷貝對象,生成新的__NSCFString對象(內(nèi)存地址會發(fā)生變化)。
2、testStr12與testStr22、testStr13、testStr12具有相同的內(nèi)存地址(0x91472c787c5ba8b1)。對NSTaggedPointerString對象進行copy操作,不引起引用計數(shù)變化;進行mutableCopy操作,會拷貝對象,生成新的__NSCFString對象(內(nèi)存地址會發(fā)生變化)。
3、比較有意思的是testStr31與testStr33具有相同的內(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