測試結(jié)論
- 常量字符串在正式賦值之前就存在,會保留到整個程序運(yùn)行期間,無法清除。
- 在調(diào)用函數(shù)結(jié)束的時候NSString字符串才會自動清空,之前都能在內(nèi)存中搜到。
- 非常量字符串可以通過念茜的CFStringGetCStringPtr的方式清空。(念茜方法詳情
只想了解怎么清除NSString的朋友看到這里就夠了,下面是以上結(jié)論的測試過程。
引子
最近手頭的iOS項(xiàng)目對安全性要求非常高。
某個敏感數(shù)據(jù)使用NSString存儲,非常敏感,必須即用即清理。NSString對象會通過ARC自動釋放,而并沒有顯式的清除方法,所以并不能確定自動釋放之后內(nèi)容是否自動清除。
找到念茜博主提供的NSString字符串清除。
為了驗(yàn)證方法有效性,我直接使用念茜的代碼試驗(yàn)
_text = @"12dfavkasie";
memset((__bridge voidvoid *)(_text), 0, _text.length - 1);
NSString *myString = [[NSString alloc]initWithFormat:@"information"];
NSLog(@"Origal text : %@ \n",myString); //斷點(diǎn)1
“Origal text :”沒有顯示字符串內(nèi)容,但是在斷點(diǎn)1進(jìn)行內(nèi)存掃描依然能搜到一個12dfavkasie。所以為了驗(yàn)證該方法有效性,需要解決如下幾個問題。
- 斷點(diǎn)1搜到的字符串是常量字符串還是NSString沒擦除的結(jié)果。
- NSString是否在最后一次調(diào)用的時候自動擦除。
- 使用非常量字符串來排除常量字符串干擾,使用這種方法依然能夠清除就能證明該方法可以有效清除NSString。
首先建立可以進(jìn)行內(nèi)存掃描的iOS環(huán)境。
iOS掃描內(nèi)存過程
- 使用盤古越獄將手頭iPhone 4s iOS 7.1.2 越獄。
- 在Cydia上下載OpenSSH。
- 在Cydia上添加源:http://grantdouglas.co.uk 下載 memscan。
- 在MAC上使用SSH連接OpenSSH。
ssh root@192.168.2.199
iPhone手機(jī)默認(rèn)密碼為 :alpine
RA手機(jī),修改為:Rongan1234 - 運(yùn)行iOS中間件工具。
- 用工具名字, 獲取iOS工具PID如下:
ps-ax | grep SignTool
ps-ax | grep Mgr - 獲取的PID對應(yīng)進(jìn)程的內(nèi)存。
memscan -p 64685 -d -o out4.bin - 搜索獲取內(nèi)存中的數(shù)據(jù)
grep -c 12345677 out4.bin
得到數(shù)據(jù)為0,則內(nèi)存中找不到該字符串。
得到數(shù)據(jù)不為0,使用grep -a查看該字符串出現(xiàn)的具體位置。
測試過程
編寫用于測試的程序驗(yàn)證如下三個問題:
- 斷點(diǎn)1搜到的字符串是常量字符串還是NSString沒擦除的結(jié)果。
通過程序1確定常量字符串的特性。 - NSString是否在最后一次調(diào)用的時候自動擦除。
建立兩個NSString,確定兩個字符串清除的時間。 - 使用非常量字符串來排除常量字符串干擾,使用這種方法依然能夠清除就能證明該方法可以有效清除NSString。
常量字符串是否一直存在?
NSString *testStr = [[NSString alloc] initWithFormat:@"14642334"];
NSString *testStr1 = [[NSString alloc] initWithFormat:@"sdfsafd"];
NSString *testStr2 = [[NSString alloc] initWithFormat:@"dsfdsafd"];
NSString *testStr3 = [[NSString alloc] initWithFormat:@"dncmviwmaf"];
NSLog(@"test1");
NSLog(@"test2");
NSLog(@"test1");
NSLog(@"test2");
[testStr compare:testStr1];
[testStr compare:testStr2];
[testStr compare:testStr3];
在該段代碼運(yùn)行之前,用memscan獲取內(nèi)存,用grep -C 10 14642334 獲取該字符串附近的上下文,結(jié)果如下。
lengthOfBytesUsingEncoding:
getCString:maxLength:encoding:
keyEnumerator
nextObject
test
14642334
sdfsafd
dsfdsafd
dncmviwmaf
test1
test2
當(dāng)該函數(shù)運(yùn)行結(jié)束之后,查找14642334,結(jié)果一樣。
結(jié)論
常量字符串特點(diǎn)如下:
- 集中存儲在一個位置。
- 無論代碼有沒有運(yùn)行到常量字符串所在位置,內(nèi)存中都會包含這個字符串的內(nèi)容。
所以之前念茜代碼最后搜到的測試字符串應(yīng)該是常量字符串。
NSString是否自動清除
測試代碼:
unsigned char testByteStr[10];//斷點(diǎn)0
testByteStr[0] = '1';//14642334
testByteStr[1] = '4';
testByteStr[2] = '6';
testByteStr[3] = '4';
testByteStr[4] = '2';
testByteStr[5] = '3';
testByteStr[6] = '3';
testByteStr[7] = '4';
NSString *testStr = [[NSString alloc] initWithBytes:testByteStr length:8 encoding:NSASCIIStringEncoding];
testByteStr[0] = '4';//47951438
testByteStr[1] = '7';
testByteStr[2] = '9';
testByteStr[3] = '5';
testByteStr[4] = '1';
testByteStr[5] = '4';
testByteStr[6] = '3';
testByteStr[7] = '8';
NSString *testStr2 =[[NSString alloc] initWithBytes:testByteStr length:8 encoding:NSASCIIStringEncoding];
memset(testByteStr, 0, 8);
NSLog(@"test");//斷點(diǎn)1
[testStr compare:testStr2];
NSLog(@"test");//斷點(diǎn)2
NSLog(@"%l",testStr.length);
NSLog(@"test");//斷點(diǎn)3
testStr = @"123";
NSLog(@"test");//斷點(diǎn)4
結(jié)果和分析:
斷點(diǎn)0時,14642334出現(xiàn)0次,47951438出現(xiàn)0次。
斷點(diǎn)1時,14642334出現(xiàn)1次,47951438出現(xiàn)1次。
testByteStr已經(jīng)用memset清空,所以內(nèi)存中唯一的14642334是testStr。唯一的47951438是testStr2。
斷點(diǎn)2時,14642334出現(xiàn)1次,47951438出現(xiàn)1次。
之后testStr2最后一次調(diào)用在斷點(diǎn)2前,然而testStr2依然能搜到。所以NSString并沒有在最后一次調(diào)用時自我清除。
斷點(diǎn)3時,14642334出現(xiàn)1次,47951438出現(xiàn)1次。
斷點(diǎn)4時,14642334出現(xiàn)1次,47951438出現(xiàn)1次。
斷點(diǎn)5為該函數(shù)執(zhí)行結(jié)束之后的斷點(diǎn),14642334出現(xiàn)0次,47951438出現(xiàn)0次。
結(jié)論
NSString只會在所在函數(shù)段結(jié)束的時候自動清空。
排除常量字符串干擾,擦除字符串
unsigned char testByteStr[10];//斷點(diǎn)0
testByteStr[0] = '1';//14642334
testByteStr[1] = '4';
testByteStr[2] = '6';
testByteStr[3] = '4';
testByteStr[4] = '2';
testByteStr[5] = '3';
testByteStr[6] = '3';
testByteStr[7] = '4';
NSString *testStr = [[NSString alloc] initWithBytes:testByteStr length:8 encoding:NSASCIIStringEncoding];
memset(testByteStr , 0 , 8);
char *str = malloc(9*sizeof(char));//斷點(diǎn)1
str = (char *)CFStringGetCStringPtr((CFStringRef)testStr,CFStringGetSystemEncoding());
NSLog(@"test");//斷點(diǎn)2
memset(str , 0 , 7);
NSLog(@"test");//斷點(diǎn)3
在Xcode里運(yùn)行測試程序,在程序運(yùn)行到斷點(diǎn)0,1,2,3的時候使用memscan掃描14642334的數(shù)量。
斷點(diǎn)0結(jié)果:0
斷點(diǎn)1結(jié)果:1
testByteStr已經(jīng)清空。內(nèi)存中唯一14642334是testStr。
斷點(diǎn)2結(jié)果:1
執(zhí)行CFStringGetCStringPtr之后,內(nèi)存中依然只有一個14642334。而之前已經(jīng)試驗(yàn)過testStr字符串在函數(shù)結(jié)束之后才會清空,所以str指向testStr存儲14642334的內(nèi)存位置。
斷點(diǎn)3結(jié)果:0
清空str之后,testStr也清空了。
結(jié)論
念茜的方法可以有效清空NSString。
測試結(jié)論
- 常量字符串在正式賦值之前就存在,會保留到整個程序運(yùn)行期間,無法清除。
- 在調(diào)用函數(shù)結(jié)束的時候NSString字符串才會自動清空,之前都能在內(nèi)存中搜到。
- 非常量字符串可以通過CFStringGetCStringPtr的方式清空。