iOS基礎(chǔ)深入補(bǔ)完計(jì)劃--屬性strong&&copy聲明相關(guān)延伸

前文地址:《iOS基礎(chǔ)深入補(bǔ)完計(jì)劃》

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

以下、將分析以下幾點(diǎn):

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

關(guān)于引用計(jì)數(shù)

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

結(jié)果

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

打?。?/p>

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

打印:

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

結(jié)論:

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

這兩點(diǎn)、可以從變量地址上鑒別。


關(guān)于變量地址

  • 先用mutable字符串進(jìn)行測(cè)試:
  #define TLog(prefix,Obj) {NSLog(@"變量值地址:%p, 指向?qū)ο笾担?@, 變量類型:%@--%@",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(@"原對(duì)象mOStr", mOStr);
  self.strongedStr = mOStr;
  self.copyedStr = mOStr;
  
  TLog(@"改變前的原對(duì)象mOStr", mOStr);
  TLog(@"原對(duì)象改變前的strongedStr",_strongedStr);
  TLog(@"原對(duì)象改變前的copyedStr",_copyedStr);
  
  [mOStr appendString:@"lalala"];
  
  TLog(@"改變后的原對(duì)象mOStr", mOStr);
  TLog(@"原對(duì)象改變后的strongedStr",_strongedStr);
  TLog(@"原對(duì)象改變后的copyedStr",_copyedStr);
  }

打印:

  變量值地址:0x604000247500, 指向?qū)ο笾担?234567890, 變量類型:__NSCFString--原對(duì)象mOStr
  變量值地址:0x604000247500, 指向?qū)ο笾担?234567890, 變量類型:__NSCFString--改變前的原對(duì)象mOStr
  變量值地址:0x604000247500, 指向?qū)ο笾担?234567890, 變量類型:__NSCFString--原對(duì)象改變前的strongedStr
  變量值地址:0x6040000346e0, 指向?qū)ο笾担?234567890, 變量類型:__NSCFString--原對(duì)象改變前的copyedStr
  變量值地址:0x604000247500, 指向?qū)ο笾担?234567890lalala, 變量類型:__NSCFString--改變后的原對(duì)象mOStr
  變量值地址:0x604000247500, 指向?qū)ο笾担?234567890lalala, 變量類型:__NSCFString--原對(duì)象改變后的strongedStr
  變量值地址:0x6040000346e0, 指向?qū)ο笾担?234567890, 變量類型:__NSCFString--原對(duì)象改變后的copyedStr
  • 再用immutable字符串進(jìn)行測(cè)試:

打?。?/p>

  變量值地址:0x60000022cd20, 指向?qū)ο笾担?234567890, 變量類型:__NSCFString--原對(duì)象mOStr
  變量值地址:0x60000022cd20, 指向?qū)ο笾担?234567890, 變量類型:__NSCFString--改變前的原對(duì)象mOStr
  變量值地址:0x60000022cd20, 指向?qū)ο笾担?234567890, 變量類型:__NSCFString--原對(duì)象改變前的strongedStr
  變量值地址:0x60000022cd20, 指向?qū)ο笾担?234567890, 變量類型:__NSCFString--原對(duì)象改變前的copyedStr
  變量值地址:0x106a75130, 指向?qū)ο笾担簂alala, 變量類型:__NSCFConstantString--改變后的原對(duì)象mOStr
  變量值地址:0x60000022cd20, 指向?qū)ο笾担?234567890, 變量類型:__NSCFString--原對(duì)象改變后的strongedStr
  變量值地址:0x60000022cd20, 指向?qū)ο笾担?234567890, 變量類型:__NSCFString--原對(duì)象改變后的copyedStr

結(jié)論:

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

copy的具體實(shí)現(xiàn)

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

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

WechatIMG213.jpeg

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

因?yàn)槠胀ㄗ兞勘旧砭筒荒鼙恍薷?。也不能使用copy方法


一些題外話

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

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

NSCFConstantString

總而言之:

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

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

更多的測(cè)試、可以參閱這一片大佬的博客《NSString特性分析學(xué)習(xí)》

NSTaggedPointerString

我們可以拋開后面的String、單純的來(lái)看TaggedPointer(標(biāo)記指針)對(duì)象。因?yàn)槌薔SString、NSNumber和NSDate也有相應(yīng)的TaggedPointer對(duì)象。
簡(jiǎn)單來(lái)說(shuō):

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

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


最后

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

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

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