iOS基礎深入補完計劃--屬性strong&&copy聲明相關延伸

前文地址:《iOS基礎深入補完計劃》

在前文、我們提到了property中的關鍵字copy可以用來修飾不可變對象、以保護對象的封裝性。
那么、copy和strong修飾的屬性究竟有什么區(qū)別。為什么copy修飾之后、不受原變量的影響。

以下、將分析以下幾點:

  • 引用計數(shù)
  • 變量地址
  • copy的具體實現(xiàn)
  • 順帶分析兩個字符串的類型NSTaggedPointerString/NSCFConstantString

關于引用計數(shù)

先用mutable字符串進行測試:
  • strong修飾
  NSMutableString * mOStr = [NSMutableString stringWithFormat:@"1234567890"];
  printf("mOStr原始引用前計數(shù): %ld\n",CFGetRetainCount((__bridge CFTypeRef)(mOStr)));
  self.strongedStr = mOStr;
  printf("mOStr被strong引用后計數(shù): %ld\n",CFGetRetainCount((__bridge CFTypeRef)(mOStr)));

結果

  mOStr原始引用前計數(shù): 2
  mOStr被strong引用后計數(shù): 3
  _strongedStr計數(shù): 3
  • copy修飾
  NSMutableString * mOStr = [NSMutableString stringWithFormat:@"1234567890"];
  printf("mOStr原始引用前計數(shù): %ld\n",CFGetRetainCount((__bridge CFTypeRef)(mOStr)));
  self.copyedStr = mOStr;
  printf("mOStr被copy引用后計數(shù): %ld\n",CFGetRetainCount((__bridge CFTypeRef)(mOStr)));

打?。?/p>

  mOStr原始引用前計數(shù): 2
  mOStr被copy引用后計數(shù): 2
  _copyedStr計數(shù): 1
再用immutable字符串進行測試:
  NSString * mOStr = [NSString stringWithFormat:@"1234567890"];
  printf("mOStr原始引用前計數(shù): %ld\n",CFGetRetainCount((__bridge CFTypeRef)(mOStr)));
  self.strongedStr = mOStr;
  printf("_mOStr被strong引用后計數(shù): %ld\n",CFGetRetainCount((__bridge CFTypeRef)(mOStr)));
  printf("_strongedStr計數(shù): %ld\n",CFGetRetainCount((__bridge CFTypeRef)(_strongedStr)));
  self.copyedStr = mOStr;
  printf("_mOStr被copy引用后計數(shù): %ld\n",CFGetRetainCount((__bridge CFTypeRef)(mOStr)));
  printf("_copyedStr計數(shù): %ld\n",CFGetRetainCount((__bridge CFTypeRef)(_copyedStr)));

打?。?/p>

  mOStr原始引用前計數(shù): 2
  _mOStr被strong引用后計數(shù): 3
  _strongedStr計數(shù): 3
  _mOStr被copy引用后計數(shù): 4
  _copyedStr計數(shù): 4

結論:

  • 在mutable字符串下。使用strong修飾會增加原對象引用計數(shù)、使用copy修飾則不會。
  • 在immutable下。使用strong/copy修飾都會增加原對象引用計數(shù)。
  • 除了字符串以外、Array/Dictionary的測試結果相同。
至于為什么在immutable下、引用都會+1。有兩種可能。
  • 像__block一樣、將原對象轉移到堆中并且將全部指針以及計數(shù)轉移到block內(nèi)的只針對想上。
  • 單純對原對象增加了引用計數(shù)。

這兩點、可以從變量地址上鑒別。


關于變量地址

  • 先用mutable字符串進行測試:
  #define TLog(prefix,Obj) {NSLog(@"變量值地址:%p, 指向對象值:%@, 變量類型:%@--%@",Obj,Obj,[Obj class],prefix);}
  
  @interface ViewController ()
  
  @property (nonatomic,strong,readwrite) NSString * strongedStr;
  @property (nonatomic,copy,readwrite) NSString * copyedStr;
  
  @end
  
  @implementation ViewController
  
  - (void)viewDidLoad {
  [super viewDidLoad];
  // Do any additional setup after loading the view, typically from a nib.
  
  NSMutableString * mOStr = [NSMutableString stringWithFormat:@"1234567890"];
  TLog(@"原對象mOStr", mOStr);
  self.strongedStr = mOStr;
  self.copyedStr = mOStr;
  
  TLog(@"改變前的原對象mOStr", mOStr);
  TLog(@"原對象改變前的strongedStr",_strongedStr);
  TLog(@"原對象改變前的copyedStr",_copyedStr);
  
  [mOStr appendString:@"lalala"];
  
  TLog(@"改變后的原對象mOStr", mOStr);
  TLog(@"原對象改變后的strongedStr",_strongedStr);
  TLog(@"原對象改變后的copyedStr",_copyedStr);
  }

打印:

  變量值地址:0x604000247500, 指向對象值:1234567890, 變量類型:__NSCFString--原對象mOStr
  變量值地址:0x604000247500, 指向對象值:1234567890, 變量類型:__NSCFString--改變前的原對象mOStr
  變量值地址:0x604000247500, 指向對象值:1234567890, 變量類型:__NSCFString--原對象改變前的strongedStr
  變量值地址:0x6040000346e0, 指向對象值:1234567890, 變量類型:__NSCFString--原對象改變前的copyedStr
  變量值地址:0x604000247500, 指向對象值:1234567890lalala, 變量類型:__NSCFString--改變后的原對象mOStr
  變量值地址:0x604000247500, 指向對象值:1234567890lalala, 變量類型:__NSCFString--原對象改變后的strongedStr
  變量值地址:0x6040000346e0, 指向對象值:1234567890, 變量類型:__NSCFString--原對象改變后的copyedStr
  • 再用immutable字符串進行測試:

打?。?/p>

  變量值地址:0x60000022cd20, 指向對象值:1234567890, 變量類型:__NSCFString--原對象mOStr
  變量值地址:0x60000022cd20, 指向對象值:1234567890, 變量類型:__NSCFString--改變前的原對象mOStr
  變量值地址:0x60000022cd20, 指向對象值:1234567890, 變量類型:__NSCFString--原對象改變前的strongedStr
  變量值地址:0x60000022cd20, 指向對象值:1234567890, 變量類型:__NSCFString--原對象改變前的copyedStr
  變量值地址:0x106a75130, 指向對象值:lalala, 變量類型:__NSCFConstantString--改變后的原對象mOStr
  變量值地址:0x60000022cd20, 指向對象值:1234567890, 變量類型:__NSCFString--原對象改變后的strongedStr
  變量值地址:0x60000022cd20, 指向對象值:1234567890, 變量類型:__NSCFString--原對象改變后的copyedStr

結論:

  • 在mutable字符串下。使用strong修飾引用指向原變量內(nèi)存地址
    copy則會在新地址上copy出一個變量
  • 在immutable下。使用strong/copy修飾都不會生成新的變量、所以指針是指向了同一個地址并且將計數(shù)+1。
  • 無論聲明的屬性可變與否、copy聲明讓你得到一份不可變的副本。
  • 除了字符串以外、Array/Dictionary的測試結果相同。

copy的具體實現(xiàn)

  • 網(wǎng)上都說是在set內(nèi)部實現(xiàn)了[xxx copy]動作。

自己試了試、提示未式襲擊案copyWithZone方法。確實是如網(wǎng)上所言。

WechatIMG213.jpeg

  • 所以、copy屬性不支持普通變量

因為普通變量本身就不能被修改。也不能使用copy方法


一些題外話

在測試地址的過程中、又出現(xiàn)兩個問題。

  • NSCFConstantString是個什么東西。
  • NSTaggedPointerString又是個什么。(如果將str的內(nèi)容由1234567890縮短成1234。NSCFString就會變成NSTaggedPointerString類型)

NSCFConstantString

總而言之:

  • NSCFConstantString類型的出現(xiàn)、取決于你這個字符串的創(chuàng)建方式。具體點:

    NSString * mOStr1 = @"1234";
    NSString * mOStr2 = [NSString stringWithString:@"1234"];
    NSString * mOStr3 = [NSString stringWithFormat:@"1234"];//這個是正常生成的、是個對象。
    
  • 先說第1、2種方式聲明的字符串:
  • 以上兩種方式生成的字符串、無論長短都是NSCFConstantString類型。
  • 引用計數(shù)無限大。
  • 在值相同的情況下可以直接用 ‘==’判定。
  • 經(jīng)測試、值相同時、變量地址相同。都存在于棧內(nèi)存上。(應該就是個常量字符串吧)
  • 然后、最后一種方式生成的字符串:
  • 其他objc對象類似的、在堆上分配內(nèi)存。
  • 初始引用計數(shù)為1。

更多的測試、可以參閱這一片大佬的博客《NSString特性分析學習》

NSTaggedPointerString

我們可以拋開后面的String、單純的來看TaggedPointer(標記指針)對象。因為除了NSString、NSNumber和NSDate也有相應的TaggedPointer對象。
簡單來說:

  • TaggedPointer用于存儲所需字節(jié)較小的變量型對象。
  • TaggedPointer用于將數(shù)據(jù)直接保存在指針地址本身中(指針的值不再單純是地址了,而是真正的值)、借此不需要生成對象、節(jié)省了內(nèi)存和效率。
  • TaggedPointer只是一個披著對象皮的普通變量而已。

參考《深入理解Tagged Pointer》、《【譯】采用Tagged Pointer的字符串》


最后

本文主要是自己的學習與總結。如果文內(nèi)存在紕漏、萬望留言斧正。如果不吝賜教小弟更加感謝。

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

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