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.