再探NSString

再探NSString

NSString應該是oc開發中最經常使用的一個數據類型了,此次對該類型再進行一次全方位的探索與總結。設計模式

NSString本質上屬於OC類對象,繼承於NSObject,遵照NSCopying, NSMutableCopying, NSSecureCoding協議。架構

NSMutableString與之相似,惟一不一樣的是它繼承於NSString。框架

經過語法糖建立NSString

大部分的開發中,咱們都會使用@+雙引號的方式建立NSString對象。以下:設計

NSString* str=@"i am a single str";

這種建立方式建立出來的類型是什麼呢?它存儲的空間地址又在哪裏呢?不妨打印一下:3d

NSLog(@"%@:%p",[str class],str);
//打印以下:
//__NSCFConstantString:0x100002058

爲何明明是NSString類型的在這裏會變成__NSCFConstantString類型,這是由於NSString實際上是個類族(大部分容器類也是如此),它的初始化方法返回的實例對象實際上是隱藏在類族中的公共接口後面的某一內部類型。指針

類族(class cluster):屬於一種設計模式,用以隱藏「抽象基類」背後的實現細節。oc的系統框架中廣泛使用此模式。code

其實__NSCFConstantString類型是一種字符串的常量類型,當切換成MRC模式下使用retainCount會發現它的引用計數會很是大,一般爲:2^64-1,這樣設置的緣由就是不管對其進行屢次release都可以保證對象不會被釋放,因此能夠直接將其看做爲一個單例。對於單例對象,那麼只要有相同的內容,他們的內存地址也就相同。以下所示:orm

NSString* str0=@"i am not a single str";
NSString* str1=@"i am not a single str";
NSLog(@"%@:%p",[str0 class],str0);
NSLog(@"%@:%p",[str1 class],str1);

//打印以下:
//__NSCFConstantString:0x100002050
//__NSCFConstantString:0x100002050

這些字符串對象都存儲於字符串常量區。對象

OC有五大內存管理區域,地址由小到大分別爲:代碼段、數據段、BSS段、堆、棧。blog

__NSCFConstantString類型的字符串常量就存儲於數據段當中的常量區。

經過stringWithFormat建立NSString

一般在開發中,須要將獲得的臨時變量或者類對象的字符串進行拼接時會用到這個類方法class method。

那麼這種建立方式建立出來的類型是什麼呢?它存儲的空間地址又在哪裏呢?不妨再次打印一下:

NSString* str=[NSString stringWithFormat:@"hi"];
NSLog(@"%@:%p",[str class],str);

//打印以下:
NSTaggedPointerString:0x723673ffe0bc6c91

說到NSTaggedPointerString這個類型,就要說到標籤指針(tagged pointer)了,蘋果在推出64位架構的A7雙核處理器,也就是5s的時候,爲了節省內存和提升執行效率,蘋果提出了Tagged Pointer的概念來標註特定類型的數值。

標籤指針:標籤指針在不使用原來類型對象的狀況下,把與數值有關的所有消息都放在指針值裏面。運行期系統會在消息派發期間檢測到這種標籤指針。除了NSString外,NSNumber、NSDate類型也會採用該策略。

那麼爲何原有對象會浪費內存?咱們能夠拿NSNumber對象來舉個例子。衆所周知,NSNumber的佔位與CPU的位數有關。在32位機器上整數會佔4個字節,64位上則佔8個字節。因此直接採起原有對象類型來存儲,從32位機遷移到64位機後,NSNumber的對象佔用的內存空間就會加倍。

具體存儲策略以下圖所示:

在64位機器上,咱們能夠將採用標籤指針策略的對象當作由兩個部分的指針組成。第一個部分用來存儲特殊標記,表示這是一個採用了標籤指針策略的指針(不指向任何內存地址),第二個部分剩餘的內存大小均可以用做存儲數據。

可是當字符串內容超過了指針範圍後,就不會再採起標籤指針的策略了。例如初始化一個很長的德語單詞:

NSString* str=[NSString stringWithFormat:@"Kraftfahrzeughaftpflichtversicherung"];
NSLog(@"%@->%@->%@->%@:%p",[str class],[[str class] superclass],[[[str class] superclass] superclass],[[[[str class] superclass] superclass] superclass],str);

//打印以下:
__NSCFString->NSMutableString->NSString->NSObject:0x103825090

由此可知,__NSCFString繼承於NSMutableString,而NSMutableString又繼承於NSString。之因此會出現這種情況也就是由於oc的系統框架中使用了類族的模式。通常狀況下能夠把該類型直接看作爲NSString類型,該對象存儲於堆中。

不一樣類型的NSString存儲位置

類名 存儲位置 初始化引用計數
__NSCFString 1
NSTaggedPointerString 2^64-1
__NSCFConstantString 常量區 2^64-1

總結

  • 採用語法糖建立NSString會以單例模式存儲於常量區。
  • 當NSString採用stringWithFormat來建立對象時,系統會優先採起標籤指針策略來存儲對象,當對象的內容和Flag大小超出了指針大小,則採用常規方式存儲oc對象。
  • 不管採用什麼方式初始化NSString,它的類型都不會是NSString類型,由於系統生成NSString的接口都是隱藏在類族中的公共接口。因此通常不會使用 [str class]==[NSString class] [str isMemberOfClass:[NSString class]]來判斷str類型,而是採用 [str isKindOfClass:[NSString class]]來判斷。

參考文章:
1.NSTaggedPointerString
2.MemoryAllocation

相關文章
相關標籤/搜索