有關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,這樣子還會引起循環引用,這種錯覺很難發現。
4.@weakify與@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)(10 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"%@", self.str);
});
};
self.block();
}