iOS 开发中的循环引用
iOS 开发中的循环引用
在 iOS 开发中,
retain cycle(循环引用)是一个常见的内存管理问题,尤其是在使用 ARC (Automatic Reference Counting) 进行内存管理的环境下。如果两个对象相互持有强引用 (strong),它们都不会被 ARC 释放,从而导致内存泄漏。
什么是循环引用?
在 Objective-C 中,循环引用通常发生在 block 和 delegate 之间。让我们来看一个典型的 block 相关的 retain cycle 示例。
示例:Block 引起的 Retain Cycle
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#import <Foundation/Foundation.h>
@interface MyClass : NSObject
@property (nonatomic, strong) void (^myBlock)(void);
- (void)setupBlock;
@end
@implementation MyClass
- (void)setupBlock {
self.myBlock = ^{
NSLog(@"%@", self); // 这里的 `self` 被 `block` 强引用
};
}
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
MyClass *obj = [[MyClass alloc] init];
[obj setupBlock];
}
return 0;
}
在上面的代码中,self.myBlock 对 block 进行了强引用,而 block 也捕获了 self,导致循环引用,MyClass 的实例无法被释放。
如何避免循环引用?
方法 :使用 __weak 解决 Retain Cycle
Objective-C 提供了 __weak 关键字,可以让 self 以 弱引用(weak reference) 的方式被 block 捕获,从而避免 retain cycle。
1
2
3
4
5
6
- (void)setupBlock {
__weak typeof(self) weakSelf = self;
self.myBlock = ^{
NSLog(@"%@", weakSelf); // weakSelf 不是强引用
};
}
为什么 __weak 可以打破循环?
因为
__weak只是一个 非强引用,它不会增加对象的引用计数,因此即使block还存在,也不会阻止self被释放。
循环引用的另一种常见场景:Delegate
循环引用不仅发生在 block,也可能出现在 delegate 模式,如果 A 持有 B 的 delegate,而 B 强引用 A,就会发生循环引用。
示例:Delegate 引起的 Retain Cycle
1
2
3
4
5
6
7
8
9
10
11
12
13
14
@interface MyViewController : UIViewController
@property (nonatomic, strong) UITableView *tableView;
@end
@implementation MyViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.tableView = [[UITableView alloc] initWithFrame:self.view.bounds];
self.tableView.delegate = self; // 强引用可能会导致循环引用
[self.view addSubview:self.tableView];
}
@end
如何解决?
1
@property (nonatomic, weak) id<UITableViewDelegate> delegate;
因为 delegate 通常是一个指向 self 的指针,我们应使用 weak 修饰以防止 retain cycle。
总结
- 循环引用发生在 对象相互强引用 的情况下,常见于 block 和 delegate。
- 使用
__weak可以避免block持有self,从而防止retain cycle。 - 在
delegate设计模式中,使用weak来修饰delegate以避免循环引用。
This post is licensed under CC BY 4.0 by the author.