initWithString、initWithFormat及stringWithFormat的區(qū)別

其實這幾個方法看似沒啥區(qū)別,用的時候也很少在意,但最近無聊折騰了一下,卻發(fā)現(xiàn)有些端倪。這里先說Apple的一個優(yōu)化策略:Tagged Pointer,這里引用大神的文章: iOS Tagged Pointer - 簡書。簡單來說就是運行時讓能用指針地址表達的值就用指針地址表達,不再單獨分配內(nèi)存地址,上個代碼先:

以a的ascii碼為基礎(chǔ),依次加一,然后根據(jù)新的ascii碼生成字符串a(chǎn)、b、c.....i、j,并打印他們的相關(guān)信息,打印結(jié)果如下:


打印結(jié)果

可以看到,通過stringWithFormat創(chuàng)建的字符串的類型是NSTaggedPointerString類型而不是__NSCFString或者__NSCFConstantString類型的。然后再看指針的地址,最后的兩位應(yīng)該是標識位,倒數(shù)第一位應(yīng)該是指定值的類型,倒數(shù)第二位表示值的長度,剩下的61到6a是遞增的,剛好符合上面代碼ascii碼的循環(huán)加一,61是“a”ascii碼的十六進制表示,這就說明了運行時使用了Tagged Pointer技術(shù)。為啥要說這些亂七八糟的東西?因為我之前不知道有Tagged Pointer這個高級的東東,在測試NSString相關(guān)方法的時候踩了一些坑,所以特此介紹,免得看這篇文章的同學(xué)被坑進去,在測試的時候盡量用中文或者長字符串,這樣就能夠讓系統(tǒng)在堆區(qū)中給字符串分配內(nèi)存空間。??

接下來回到正題,在使用字面量聲明字符串的時候,字符串是放在常量區(qū)的,在編譯階段就已經(jīng)確定,所以以下代碼的變量無論聲明多少都是引用常量區(qū)里面的同一個字符串。


代碼


打印結(jié)果

然后來看看stringWithFormat和initWithFormat,一個是類方法,一個是實例方法,stringWithFormat的內(nèi)存管理方式是autorelease,initWithFormat則需要手動釋放(ARC下不用處理,編譯器幫我們完成了)。一眼看去好像差別不大。但是stringWithFormat在下面的代碼中就有問題了:

代碼
內(nèi)存監(jiān)控

內(nèi)存出現(xiàn)了暴增的現(xiàn)象,這是由于stringWithFormat會在內(nèi)部對創(chuàng)建的字符串做一次autorelease處理,這就導(dǎo)致了對象的延遲釋放,因為這里有個for循環(huán),那么autoreleasepool會等到runloop的當前循環(huán)結(jié)束后才會對釋放池中的每個對象發(fā)送release消息,而runloop的當前循環(huán)結(jié)束的前提是要等for循環(huán)執(zhí)行完,所以for循環(huán)內(nèi)創(chuàng)建的對象就會在for循環(huán)執(zhí)行完之前一直存在在內(nèi)存中,導(dǎo)致暴增。再來看看initWithFormat:


代碼

這里編譯器會有一個警告,提示你這是一個弱引用,會在單次循環(huán)結(jié)束后被釋放(這里就已經(jīng)能夠說明問題了),接著我們來看看內(nèi)存監(jiān)控:


內(nèi)存監(jiān)控

并沒有出現(xiàn)暴增的情況,也就是說initWithFormat創(chuàng)建的對象在ARC模式中,for的單次循環(huán)結(jié)束后就會release一次并被釋放(MRC下手動管理)。

那么習(xí)慣使用stringWithFormat的同學(xué)就要注意點了,如果存在循環(huán)大量的創(chuàng)建字符串,要么我們盡量使用initWithString,非要用stringWithFormat怎么辦?其實在循環(huán)內(nèi)加個@autoreleasepool就好了(保證每次for循環(huán)都對對象release一次),就像下面這樣:


代碼

最后,關(guān)于initWithString,這個方法如果你傳的是字面量,那么編譯器會提示你其實可以直接用字面量賦值,如果傳的是變量或常量,我猜內(nèi)部應(yīng)該是返回的一個拷貝給你(參數(shù)是類型是NSString的話就是淺拷貝,NSMutableString就是深拷貝),內(nèi)部代碼我猜是這樣的:


代碼

放個測試代碼:


代碼
打印

主要看str2、str3、str4、str5,str2是字面量賦值的變量,所以值是在常量區(qū),str3是用str2創(chuàng)建的,他們的地址是一樣的,說明str3也是引用了常量區(qū)的值。str4的值是分配在堆區(qū)的,str5跟str4的地址一樣,說明引用的是str4在堆中的值地址,所以我推測,initWithString內(nèi)部只是做了一次copy。

sorry,廢話有點多,就一個小的知識點,如果之前不知道,希望能幫到你,如果你對此非常了解并對我寫的有不同的觀點,希望能幫我指出不足或錯誤。

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

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

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