作為一個開發(fā)者,我們每天都在新的需求中創(chuàng)建一個又一個Object卷雕,每個類都可能會有一些私有或共有的屬性钱雷,來滿足我們的開發(fā)需求骂铁。
那么,每一個屬性要如何來用關(guān)鍵字定義使用罩抗?每個修飾屬性的關(guān)鍵字之間又有什么區(qū)別拉庵,你真的了解嗎?
下面代碼中的修飾屬性的關(guān)鍵字套蒂,大家一定再熟悉不過了钞支,如果你不熟悉,騷年您轉(zhuǎn)行吧!
@interface ViewController ()
@property (nonatomic, assign) NSInteger persionAge;
@property (nonatomic, copy) NSString *persionName;
@property (nonatomic, weak) UIImageView *persionIconView;
@property (nonatomic, strong) NSArray *persionArray;
@end
下面Zombie代大家詳細(xì)了解一下操刀,assign烁挟、copy、weak骨坑、strong之間的同處撼嗓、異處。
是否會開辟新的內(nèi)存
是否會對地址引用計數(shù)增加
哪里講的不好欢唾,歡迎大家留言指出且警、愿意和大家共同探討!
一. 我們舉一個典型的例子非容器可變變量'NSMutableString'
來了解一下礁遣。
@interface ViewController ()
@property (nonatomic, copy) NSString *strCopy;
@property (nonatomic, strong) NSString *strStrong;
@property (nonatomic, weak) NSString *strWeak;
@property (nonatomic, assign) NSString *strAssign;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
NSMutableString *testStr = [NSMutableString stringWithString:@"address"];
self.strCopy= testStr;
self.strStrong= testStr;
self.strWeak= testStr;
self.strAssign= testStr;
// 輸出結(jié)果
NSLog(@"testStr 輸出:%p, %@", testStr, testStr);
NSLog(@"strCopy 輸出:%p, %@",_strCopy, _strCopy);
NSLog(@"strStrong 輸出:%p, %@",_strStrong,_strStrong);
NSLog(@"strWeak 輸出:%p, %@",_strWeak, _strWeak);
NSLog(@"strAssign 輸出:%p, %@",_strAssign,_strAssign);
NSLog(@"引用計數(shù)為%@",[testStr valueForKey:@"retainCount"]);
// 輸出內(nèi)容
testStr 輸出:0x60400005d0d0, address
strCopy 輸出:0xa737365726464617, address
strStrong 輸出:0x60400005d0d0, address
strWeak 輸出:0x60400005d0d0, address
strAssign 輸出:0x60400005d0d0, address
引用計數(shù)為2
}
以上結(jié)果我們看出
-
strStrong
斑芜、strWeak
、strAssign
指針指向的地址都與testStr
相同 -
copy
修飾的strCopy
祟霍,賦值后則是自己單獨開辟了一塊內(nèi)存地址杏头,內(nèi)存上保存address
字符串,并指向它沸呐。 -
testStr
的引用計數(shù)為2醇王,并不為4;strWeak
垂谢、strAssign
指針雖然都指向了testStr
的內(nèi)存地址,但并不會使testStr
的引用計數(shù)增加厦画。
根據(jù)以上結(jié)果我們可以看出,當(dāng)我們現(xiàn)在修改了testStr的值滥朱,勢必不會對strCopy的值造成影響根暑,OK我們來測試一下。
[testStr stringByAppendingString:@"_xxoo"];
NSLog(@"testStr 輸出:%p, %@", testStr, testStr);
NSLog(@"strCopy 輸出:%p, %@",_strCopy, _strCopy);
NSLog(@"strStrong 輸出:%p, %@",_strStrong, _strStrong);
NSLog(@"strWeak 輸出:%p, %@",_strWeak, _strWeak);
NSLog(@"strAssign 輸出:%p, %@",_strAssign, _strAssign);
// 輸出內(nèi)容
testStr 輸出:0x60400005d0d0, address_xxoo
strCopy 輸出:0xa737365726464617, address // 未發(fā)生變化
strStrong 輸出:0x60400005d0d0, address_xxoo
strWeak 輸出:0x60400005d0d0, address_xxoo
strAssign 輸出:0x60400005d0d0, address_xxoo
- 使用
copy
會重新開辟一塊新的內(nèi)存來保存相同的數(shù)據(jù)徙邻,原內(nèi)存內(nèi)容被修改對它互相不影響排嫌。 -
strong
&&weak
都指向原來的內(nèi)存地址,所以值也會被修改; -
strong
會使數(shù)據(jù)地址引用計數(shù)+1缰犁,但weak
不會淳地。
那么引用計數(shù)+1怖糊,到底有什么實際上的不一樣呢?
我們重新實例化testStr颇象,然后來看下面一段代碼
testStr = [[NSMutableString alloc] initWithString:@"abc"];
NSLog(@"引用計數(shù)%@",[testStr valueForKey:@"retainCount"]);
NSLog(@"testStr 輸出:%p, %@", testStr, testStr);
NSLog(@"strCopy 輸出:%p, %@",_strCopy, _strCopy);
NSLog(@"strStrong 輸出:%p, %@",_strStrong, _strStrong);
NSLog(@"strWeak 輸出:%p, %@",_strWeak, _strWeak);
NSLog(@"strAssign 輸出:%p, %@",_strAssign, _strAssign);
// 輸出內(nèi)容
testStr 輸出:0x608000055c90, abc
strCopy 輸出:0xa737365726464617, address
strStrong 輸出:0x60400044df50, address_xxoo
strWeak 輸出:0x60400044df50, address_xxoo
strAssign 輸出:0x60400044df50, address_xxoo
-
strCopy
我們看出伍伤,并沒有受到任何影響 -
strStrong
、strWeak
遣钳、strAssign
不等于abc
,而等于testStr
之前的內(nèi)容扰魂,為什么呢?這是因為strStrong
使testStr
所指向的原地址引用計數(shù)+1蕴茴,所以testStr
重新實例化之后并沒有使strWeak
劝评、strAssign
的值為空。
我們在來看一段代碼倦淀,再來討論蒋畜。
testStr = [[NSMutableString alloc] initWithString:@"abc"];
self.strStrong = nil;
NSLog(@"testStr 輸出:%p, %@", testStr, testStr);
NSLog(@"strCopy 輸出:%p, %@",_strCopy, _strCopy);
NSLog(@"strStrong 輸出:%p, %@",_strStrong, _strStrong);
NSLog(@"strWeak 輸出:%p, %@",_strWeak, _strWeak);
NSLog(@"strAssign 輸出:%p, %@",_strAssign, _strAssign);
// 輸出內(nèi)容
testStr 輸出:0x60c0002464e0, abc
strCopy 輸出:0xa737365726464617, address
strStrong 輸出:0x0, (null)
strWeak 輸出:0x0, (null)
strAssign···崩潰···
- 當(dāng)設(shè)置了
strStrong
為nil后,strStrong
指向testStr
原來的那一塊內(nèi)存地址引用就會-1撞叽,造成原內(nèi)存地址釋放姻成,所以strWeak
為null了,strStrong
當(dāng)然也為null能扒。 - 當(dāng)原內(nèi)存地址釋放后佣渴,指向原內(nèi)存地址的
strAssign
產(chǎn)生程序崩潰,而strWeak
不會初斑。后面我們在講解這是什么原因。
我們稱會對數(shù)據(jù)地址增加引用計數(shù)的為強(qiáng)引用膨处,不改變引用計數(shù)的為弱引用见秤。
二. 我們來看一下assign
&&weak
的區(qū)別。
先來看一段代碼
NSMutableString *testString = [[NSMutableString alloc] initWithString:@"abc"];
self.strAssign = testString;
self.strWeak = testString;
testString = [[NSMutableString alloc] initWithString:@"test"];
NSLog(@"strWeak 輸出:%p, %@",_strWeak, _strWeak);
NSLog(@"strAssign 輸出:%p, %@",_strAssign, _strAssign);
// 輸出內(nèi)容
strWeak 輸出:0x0, (null)
strAssign···崩潰···
-
assign
如果賦值為nil的時候真椿,并不會使assign
指向的地址的數(shù)據(jù)抹除鹃答,只是對自己的值進(jìn)行釋放。 - 我們可以發(fā)現(xiàn)在輸出
strAssign
的時候會出現(xiàn)崩潰的情況突硝。
原因其實是出現(xiàn)了野指針的問題测摔,strAssign
和strWeak
相同,指向testString
的內(nèi)存地址解恰,但引用計數(shù)不+1锋八。
當(dāng)testString
重新實例化后,原指向的內(nèi)存地址引用計數(shù)為0會被釋放护盈,這個時候assign
指向的地址就不存在了挟纱,所以會發(fā)生崩潰;如果這塊內(nèi)存地址沒有被其他內(nèi)容重新占用時腐宋,assign
還可以正常輸出內(nèi)容紊服,但是一旦被從新寫入數(shù)據(jù)檀轨,該指針指向地址的內(nèi)容隨時可能沒有值,或錯亂值欺嗤,更及其容易造成崩潰参萄。
三. retain
是什么
- ARC之前屬性構(gòu)造器的關(guān)鍵字是
retain
、copy
煎饼、assign
讹挎。 -
strong
、weak
是ARC帶出來的關(guān)鍵字腺占。 -
retain
等同于strong
淤袜,指針指向值內(nèi)存地址,同時進(jìn)行引用計數(shù)加1衰伯。
四. 非NSMutableString的情況
- 我在后續(xù) 屬性關(guān)鍵字-(進(jìn)階篇) 和大家一同討論
本章小節(jié)
當(dāng)我們使用strStrong
铡羡、strWeak
、strAssign
意鲸、copy
關(guān)鍵字來修飾我們定義的屬性的時候烦周,首先要明確每個關(guān)鍵字的含義,和它們之間的不同點怎顾,再來分配給我們的屬性读慎。
再我們賦值的時候,要想清楚我們的目的槐雾,修改屬性內(nèi)容的時候也要注意修改對其他的對象的值有無其他影響夭委,這樣才能使我們的程序更在穩(wěn)定。