一、weak和strong 1.理解 剛開始學UI的時候,對於weak和strong的描述看得最多的就是“由ARC引入,weak相當於OC中的assign,但是weak用於修飾對象,但是他們都不會造成引用計數加1;而strong則相當於OC中規定retain,它會造成引用計數加1”。 ARC的...
一、weak和strong
1.理解
剛開始學UI的時候,對於weak和strong的描述看得最多的就是“由ARC引入,weak相當於OC中的assign,但是weak用於修飾對象,但是他們都不會造成引用計數加1;而strong則相當於OC中規定retain,它會造成引用計數加1”。
ARC的原理:只要還有一個變數指向對象,對象就會保持在記憶體中。當指針指向新值,或者指針不再存在時,相關聯的對象就會自動釋放。這條規則對於實例變數、synthesize屬性、局部變數都是適用的
strong指針能夠保持對象的生命,一個對象只要有strong指針指向它,那麼它就不會被釋放;相反的,如果一個沒有一個strong指針指向它,那麼它將會被自動釋放。預設所有實例變數和局部變數都是Stong指針
weak型的指針變數仍然可以指向一個對象,但不屬於對象的擁有者。即當對象被銷毀的時候,這個weak指針也就自動指向nil(空指針)。
MARK傳送門:MJ對於weak和strong的解析
2.weak和strong指針使用註意
// 我們經常看到從xib中引用到控制器的屬性都是weak型指針,為什麼那些控制項對象不會被自動釋放?
@property(nonatomic,weak) IBOOutlet UIButton *btn;
// 原來在xib中創建或放置控制項的時候,已經形成了這種引用關係
UIViewController->UIView->subView->UIButton
// 進入到UIViewcontroller.h文件中,發現
@property(null_resettable, nonatomic,strong) UIView *view; // 這貨是強引用的
// 所以,上述的引用關係就是xib對這個button是強引用,你聲明的屬性對其是弱引用
@interface LZVC ()
@property (nonatomic,weak)UIView *myView;
@end
@implementation LZVC
- (void)viewDidLoad {
[super viewDidLoad];
//出現警告:("Warning: Assigning retained object to weak variable; object will be released after assignment")
_myView = [[UIView alloc] initWithFrame:self.view.frame];
_myView.backgroundColor = [UIColor redColor];
[self.view addSubview:_myView];
} @end // 我們會發現_myView根本就沒有被添加到self.view上面,因為_myView是一個weak型指針,沒有持有對象的能力,在其等號後面初始化的那個成員變數在剛剛被初始化之後便由於沒有強指針引用它便被自動釋放了,所以_myView得到的為空。
// 更正方法:
// ①將成員屬性聲明中的weak改為strong。(直接讓_myView強引用初始化的對象,如此初始化的對象就不會被自動釋放了)
// ②將出現警告的地方改為如下所示:
// 由於所有的實例變數和局部變數預設都是strong型指針,所以myView強引用初始化的對象,而後_myView弱引用myView
UIView *myView = [[UIView alloc] initWithFrame:self.view.frame]; UIView *myView.backgroundColor = [UIColor redColor]; _myView = myView; [self.view addSubview:_myView];
3.weak和strong的使用時機(根據上面的特征,我做出如下測試)
1> 我新建了一個繼承自UIView的子類TestView,新增了一個屬性text,重寫了它的dealloc方法,我想看看TestView什麼時候釋放
@property (nonatomic,copy)NSString *text; // 屬性
// 重寫Dealloc並列印數據
-(void)dealloc
{
NSLog(@"%@----%s",self.text,__func__);
}
2> 在控制器中,我寫瞭如下代碼
#import "LZVC.h"
#import "TestView.h"
@interface LZVC ()
@property (nonatomic,weak)TestView *myWeakView; //弱引用
@property (nonatomic,strong)TestView *myStongView; //強引用
@end
@implementation LZVC
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor whiteColor];
TestView *myWeakView = [[TestView alloc] initWithFrame:CGRectMake(0, 64, 160, 160)];
myWeakView.backgroundColor = [UIColor redColor];
myWeakView.text = @"我是弱引用的";
_myWeakView = myWeakView;
[self.view addSubview:_myWeakView];
TestView *myStrongView = [[TestView alloc] initWithFrame:CGRectMake(160, 64, 160, 160)];
myStrongView.backgroundColor = [UIColor greenColor];
myStrongView.text = @"我是強引用的";
_myStongView = myStrongView;
[self.view addSubview:_myStongView];
}
#pragma mark點擊屏幕觸發
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
if (self.myWeakView) {
[self.myWeakView removeFromSuperview];
}
if (self.myStongView) {
[self.myStongView removeFromSuperview];
}
}
3> 點擊屏幕後,兩個view都從屏幕上被移除了,有如下列印,我們發現,弱引用的TestView被釋放了(因為myWeakView只有控制器對其強引用外,你聲明的屬性也對其強引用)
4> 我返回主頁,讓這個LZVC控制器被銷毀,又有列印,強引用的TestView(myStrongView除了控制器對其強引用外,
5> 總結:相信從3、4的列印中都明白了,如果你想讓一個控制項的生命周期隨著你的控制器被銷毀才去釋放,那就使用strong;如果你僅僅是想讓它在被移除之後就被銷毀,那就使用weak
二、懶載入
1.懶載入
懶載入——也稱為延遲載入,即在需要的時候才載入(效率低,占用記憶體小)。所謂懶載入,其實就是重寫getter方法。說的通俗一點,就是在開發中,當程式中需要利用的資源時。在程式啟動的時候不載入資源,只有在運行當需要一些資源時,再去載入這些資源。
我們知道iOS設備的記憶體有限,如果在程式在啟動後就一次性載入將來會用到的所有資源,那麼就有可能會耗盡iOS設備的記憶體。這些資源例如大量數據,圖片,音頻等等,所以我們在使用懶載入的時候一定要註意先判斷是否已經有了,如果沒有那麼再去進行實例化。
2.使用懶載入的好處
1> 不必將創建對象的代碼全部寫在viewDidLoad方法中,代碼的可讀性更強
2> 每個控制項的getter方法中分別負責各自的實例化處理,代碼彼此之間的獨立性強,松耦合。且其中還進行了非空判斷,防止對象被重覆載入
3> 只有當真正需要資源時,再去載入,節省了記憶體資源,防止對象被提前創建,也防止了使用對象時對象還沒被創建的問題(記憶體優化,如載入plist文件等耗記憶體的操作)。
3.使用懶載入初始化成員變數
@interface LZVC ()
@property (nonatomic,strong)NSArray *dataSource;
@end
@implementation LZVC
#pragma mark 懶載入
-(NSArray *)dataSource
{
if (_dataSource == nil) {
_dataSource = @[@"1",@"2",@"3",@"4"];
}
return _dataSource;
}
// 最後在用的時候採用self.dataSource形式方式即可
這裡順便說一說成員變數和屬性的問題:
1> 直接訪問成員變數:_dataSource = @[@"5",@"6"];
直接賦值,直觀,快捷。
2> 訪問成員屬性:self.dataSource = @[@"5",@"6"];
當進行賦值的時候會走setter方法,當獲取值的時候會走getter方法,我們可以在這兩個方法裡面做點自己想做的事情(例:在setter方法裡面控制下數據有效性、監聽值的改變等;而getter方法裡面懶載入就可以體現出其好處了。
三、迴圈引用問題(場景)
1.經典:代理模式Delegate(UITableViewDelegate)舉例
控制器的view強引用Tableview,而tableview的delegate又是控制器,如果下麵兩個代理屬性用strong去修飾,就會造成迴圈引用問題,解決這個問題的最好辦法就是兩者其中之一對其弱引用就可以了(weak)。
@property (nonatomic, weak, nullable) id <UITableViewDataSource> dataSource;
@property (nonatomic, weak, nullable) id <UITableViewDelegate> delegate;
2.NSTimer定時器Target造成迴圈引用,NSTimer會持有target對象。
3.block作為成員變數,而在block中又訪問了self或其屬性造成迴圈引用