有關ios中循環引用問題的總結
如何幹掉環
在此處不講解循環引用是什麼,請自行搜尋。ios記憶體分為堆,棧,常量區,棧和常量區都是有系統管理的。
1.delegate與環
//ClassA:
@protocol ClssADelegate
- (void)fuck;
@end
@interface ClassA : UIViewController
@property (nonatomic, strong) id delegate;
@end
//ClassB:
@interface ClassB ()
@property (nonatomic, strong) ClassA *classA;
@end
@implementation ClassB
- (void)viewDidLoad {
[super viewDidLoad];
self.classA.delegate = self;
}
解決方法:如上代碼,B強引用A,而A的delegate屬性指向B,這裡的delegate是用strong修飾的,是以A也會強引用B,這是一個典型的循環引用樣例。而解決其的方式大家也都耳熟能詳,即将delegate改為弱引用
2.block與環
@interface ClassA ()
@property (nonatomic, copy) dispatch_block_t block;
@property (nonatomic, assign) NSInteger tem;
@end
@implementation ClassA
- (void)viewDidLoad {
self.block = ^{
};
}
解決方法:
@interface ClassA ()
@property (nonatomic, copy) dispatch_block_t block;
@property (nonatomic, assign) NSInteger tem;
@end
@implementation ClassA
- (void)viewDidLoad {
__weak typeof(self) weakSelf = self
weakSelf.tem = 1;
};
}
3.weakSelf的缺陷
//ClassB是一個UIViewController,假設從ClassA pushViewController将ClassB展示出來
@interface ClassB ()
@property (nonatomic, copy) dispatch_block_t block;
@property (nonatomic, strong) NSString *str;
@end @implementation ClassB
- (void)dealloc { }
- (void)viewDidLoad {
[super viewDidLoad];
__weak typeof(self) weakSelf = self;
NSLog(@"%@", weakSelf.str);
};
}
解決方案:
這裡會有兩種情況:
• 若從A push到B,10s之内沒有pop回A的話,B中block會執行列印出來111。
• 若從A push到B,10s之内pop回A的話,B會立即執行dealloc,進而導緻B中block列印出(null)。這種情況就是使用weakSelf的缺陷,可能會導緻記憶體提前回收。
• @interface ClassB ()
@property (nonatomic, copy) dispatch_block_t block;
@property (nonatomic, strong) NSString *str;
@implementation ClassB
- (void)dealloc { }
- (void)viewDidLoad {
[super viewDidLoad];
__weak typeof(self) weakSelf = self;
self.block = ^{
__strong typeof(self) strongSelf = weakSelf; dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(10 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
});
};
self.block();
}
• 這麼做和直接用self有什麼差別,為什麼不會有循環引用:外部的weakSelf是為了打破環,進而使得沒有循環引用,而内部的strongSelf僅僅是個局部變量,存在棧中,會在block執行結束後回收,不會再造成循環引用。• 這麼做和使用weakSelf有什麼差別:唯一的差別就是多了一個strongSelf,而這裡的strongSelf會使ClassB的對象引用計數+1,使得ClassB pop到A的時候,并不會執行dealloc,因為引用計數還不為0,strongSelf仍持有ClassB,而在block執行完,局部的strongSelf才會回收,此時ClassB dealloc。
這種做法其實已經可以解決所有問題了,但是:block内部必須使用strongSelf,很麻煩,不如直接使用self簡便。很容易在block内部不小心使用了self,這樣子還會引起循環引用,這種錯覺很難發現。
[email protected]與@strongify
// 用法
@interface ClassB ()
@property (nonatomic, copy) dispatch_block_t block;
@property (nonatomic, strong) NSString *str;
@end
@implementation ClassB
- (void)dealloc { }
- (void)viewDidLoad {
[super viewDidLoad];
self.str = @"111";
@weakify(self)
self.block = ^{
@strongify(self) dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)( * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"%@", self.str);
});
};
self.block();
}