前文地址:《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)望留言斧正。如果不吝賜教小弟更加感謝。
