【iOS】用strong和weak來修飾成員變數的對比
對於純程式碼佈局,用@property宣告成員變數時,我是很自然的用strong來修飾的。然後突然有人問我用weak來修飾可不可以,我第一反應是不可以,因為用weak來修飾,初始化過後就會被釋放掉,就算我第一句寫了初始化的方法,立即執行addSubView也是沒辦法將其新增上去的。xcode也給出了很明確的警告:Assigning retained object to weak variable; object will be released after assignment。然後他又給分析了一堆記憶體管理的東西,得出的結論是可以。
由於我之前確實沒有思考過這個問題,對於這個結論我也是半信半疑,然後我就寫了個demo來驗證。自己寫demo之前我想起了之前看過的一些程式碼,有些成員變數是用weak修飾的,初始化方法是將一個臨時變數賦值給它,然後將它加到父View上,我很不理解這樣為什麼這樣寫,直接用strong修飾,然後來個懶載入初始化不是更好?或者直接初始化然後再新增?這樣算起來還少一行程式碼。。。
寫demo的過程中我又發現了一些其他的東西,首先我宣告兩個成員變數,一個strong修飾,一個weak修飾。
@interface ViewController ()
@property(nonatomic,strong) UILabel * strongLabel;
@property(nonatomic,weak) UILabel * weakLabel;
@end
strongLabel使用懶載入來初始化:
-(UILabel *)strongLabel{
if (!_strongLabel) {
_strongLabel = [[UILabel alloc] initWithFrame:CGRectMake(30, 100, 200, 60)];
_strongLabel.backgroundColor = [UIColor orangeColor];
_strongLabel.text = @"我是strong";
_strongLabel.textAlignment = NSTextAlignmentCenter;
}
return _strongLabel;
}
weakLabel直接用上面的懶載入方式來初始化
-(UILabel *)weakLabel{
if (!_weakLabel) {
//警告:Assigning retained object to weak variable; object will be released after assignment
_weakLabel = [[UILabel alloc] initWithFrame:CGRectMake(30, 120, 200, 60)];
_weakLabel.backgroundColor = [UIColor cyanColor];
_weakLabel.text = @"我是weak";
_weakLabel.textAlignment = NSTextAlignmentCenter;
}
return _weakLabel;
}
測試表明在viewDidLoad方法中呼叫addSubView方法來新增self.weakLabel時,self.weakLabel依然為nil,新增無效。
改進後的weakLabel的懶載入初始化方式:
-(UILabel *)weakLabel{
if (!_weakLabel) {
UILabel * label = [[UILabel alloc] initWithFrame:CGRectMake(30, 170, 200, 60)];
label.backgroundColor = [UIColor cyanColor];
label.text = @"我是weak";
label.textAlignment = NSTextAlignmentCenter;
_weakLabel = label;
[self.view addSubview:self.weakLabel];//如果不加這一句,下面return的時候_weakLabel依然為nil
}
return _weakLabel;
}
這樣寫之後在viewDidLoad方法中呼叫addSubView方法來新增self.weakLabel可以新增成功。
還有就是不用懶載入的方式來初始化weakLabel:
-(void)initWeakLabel{
// _weakLabel = [[UILabel alloc] initWithFrame:CGRectMake(30, 170, 200, 60)];
// _weakLabel.backgroundColor = [UIColor cyanColor];
// _weakLabel.text = @"我是weak";
// _weakLabel.textAlignment = NSTextAlignmentCenter;
// [self.view addSubview:_weakLabel];
UILabel * label = [[UILabel alloc] initWithFrame:CGRectMake(30, 170, 200, 60)];
label.backgroundColor = [UIColor cyanColor];
label.text = @"我是weak";
label.textAlignment = NSTextAlignmentCenter;
_weakLabel = label;
[self.view addSubview:self.weakLabel];//如果不加這一句,在別處呼叫_weakLabel時依然為nil
}
用weak修飾的成員變數初始化之後就會被釋放掉,所以註釋部分的程式碼是無法新增成功的。
以上這些驗證了用weak修飾UI成員變數是可以的,但是就這些而言,我也確實找不到用weak相較用strong而言更好的理由。因為這兩個label新增成功之後唯一的區別就是strongLabel的引用計數為2,weakLabel的引用計數為1,如果後期它們再做同樣的操作,我實在是想不出會有什麼情況strongLabel會出現記憶體洩漏而weakLabel不會出現記憶體洩漏的情況。
後來我試著對這兩個Label進行刪除和再新增的操作,還發現了weak修飾還是要謹慎使用,因為如果weak修飾的UI不是用懶載入來初始化的話,一旦weakLabel被removeFromSuperView了,就會變成nil,再次新增將無法顯示。雖然對這樣的操作更多的人會選擇hide屬性,但是有些場景還是需要用到removeFromSuperView。
最後結論如下:
- UI成員變數可以用weak來修飾。
- 用weak修飾的UI成員變數初始化方法與strong修飾的成員變數初始化方法不同(可參考上面的程式碼,如有更好的初始化方法也可分享)。
- 用weak修飾的成員變數呼叫removeFromSuperView會變成nil,strong修飾的呼叫removeFromSuperView變數地址不變。
- 再次呼叫被移除的weakLabel無任何效果(除非使用懶載入的方法初始化,此時weakLabel與之前被移除的weakLabel記憶體地址不同),再次呼叫被移除的strongLabel可以新增成功,地址與原地址相同。
- 也就是對於strongLabel而言,removeFromSuperView只是從View上移除它,但是它依然在記憶體中。而對於weakLabel而言,removeFromSuperView不僅是從View上移除它,也會從記憶體中刪除它。
如果某個UI移除後需要在某種條件下再次顯示初始的情況可以使用weak來修飾。
常用xib的可能比較熟悉weak,因為關聯生成的成員變數都預設是weak修飾的,也可以選擇strong,因為將Label拖到xib上就已經完成了初始化和新增的操作,此時的Label引用計數為1,相當於純程式碼的初始化和新增。以上得出的結論同樣適用於xib上的UI,所以在用xib佈局時,要注意移除在新增的操作。
面試的時候經常被問strong、weak、copy等屬性的區別,這個驗證也很好的證明了weak修飾的變數被釋放後會被置為nil,再次訪問不會發生崩潰的特性。
最後我又驗證了一下用weak來修飾陣列,直接初始化依然無法呼叫,初始化一個變數,然後將該變數賦值為weak陣列後是可以的。
驗證的測試demo地址為:https://github.com/zhanqin/strongAndWeak。
相關文章
- 對 Strong-Weak Dance的思考
- 深拷貝和淺拷貝 copy與strong修飾變數的區別變數
- Java之private關鍵字修飾成員變數Java變數
- 成員變數和區域性變數變數
- iOS strong和copy的區別iOS
- 成員變數變數
- 類的靜態成員變數和普通成員變數該怎樣去區別定義變數
- const修飾符的使用(修飾結構體指標,引用外部的const 變數)結構體指標變數
- Rust 程式設計影片教程(進階)——015_3 檢視 strong_count 和 weak_count 的改變Rust程式設計
- Java中變數之區域性變數、本類成員變數、父類成員變數的訪問方法Java變數
- Rust 程式設計視訊教程(進階)——015_3 檢視 strong_count 和 weak_count 的改變Rust程式設計
- 類,物件,成員變數和區域性變數,匿名物件物件變數
- 18、繼承以及繼承中成員變數和成員方法的重名問題繼承變數
- 成員變數、全域性變數、例項變數、類變數、靜態變數和區域性變數的區別變數
- final修飾和static final修飾的區別
- beego 模板中對變數的對比Go變數
- 12 ### 各種成員變數變數
- 類成員變數的初始化變數
- Python中類變數、成員變數、區域性變數的區別Python變數
- 訪問修飾符你用對了嗎
- private,public,protected,static不可以修飾區域性變數,方法裡的變數變數
- 12-成員變數的初始化變數
- [論文閱讀] Aligner@ Achieving Efficient Alignment through Weak-to-Strong Correction
- 子父類中成員變數變數
- c++成員變數初始化C++變數
- 類的成員變數的初始化順序變數
- 修飾符static和abstract
- 【iOS】定義@property時常用的修飾詞介紹iOS
- Java 通過反射獲取類的資訊(成員變數,成員方法,構造方法)Java反射變數構造方法
- iOS開發中使用OC和swift的對比iOSSwift
- 灰度變換函式:對數及對比度拉伸變換函式
- const關鍵字在C與C++中修飾變數的區別C++變數
- decorator(修飾器)的業務應用
- 在Python中將字典轉為成員變數的方法Python變數
- C++類的靜態成員變數初始化C++變數
- 對於靜態成員來說是類的建構函式,對於例項成員是類的原型物件。函式原型物件
- Android熱修復原理(一)熱修復框架對比和程式碼修復Android框架
- 鴻蒙 Android iOS 應用開發對比02鴻蒙AndroidiOS