彻底分析block中的循环引用

我做了一个被push到的controller,pop的时候,controller会释放,解析详见注释,欢迎批评指正!

#import "AnimaViewController.h"

@interface AnimaViewController ()
@property (nonatomic, strong) UIView *view1;
@property (nonatomic, strong) UIView *view2;
@property (nonatomic, copy) void (^block)(int);//self持有了block
@property (nonatomic, strong) UIButton *btn;
@property (nonatomic, strong) NSTimer *timer;
@end

@implementation AnimaViewController

__weak AnimaViewController *weakSelf;//用弱引用来跟踪self的释放情况。

- (void)viewDidLoad {
    weakSelf = self;//指向self的弱引用。
    
    [super viewDidLoad];
    self.view1 = [[UIView alloc]initWithFrame:CGRectMake(0, 100, 90, 90)];
    self.view1.backgroundColor = [UIColor orangeColor];
    [self.view addSubview:self.view1];
    
    self.view2 = [[UIView alloc]initWithFrame:CGRectMake(0, 300, 90, 90)];
    self.view2.backgroundColor = [UIColor blueColor];
    [self.view addSubview:self.view2];
    
    self.btn = [[UIButton alloc]init];
    self.btn.backgroundColor = [UIColor blackColor];;
    [self.btn setFrame:CGRectMake(100, 100, 80, 80)];
    [self.btn addTarget:self action:@selector(tapAction:) forControlEvents:UIControlEventTouchUpInside];
    [self.view addSubview:self.btn];
    
    //系统有时候会提示你可能导致循环引用,但会有漏掉的情况,比如strong变量定义成全局变量的时候。
    __weak AnimaViewController *weakSelfLocal = self;//弱引用,引用计数不会增加。
    self.block = ^ (int num){
        __strong AnimaViewController *strongSelf = weakSelfLocal;//增加了对self的引用计数。
        [strongSelf.btn setTitle:[NSString stringWithFormat:@"%d", num] forState:UIControlStateNormal];
        [strongSelf.btn setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
        //此后strongSelf变量作为局部strong变量,被系统发送了release消息(编译时就已插入release),所以strongSelf已经不构成对self的引用。如果将__weak AnimaViewController *weakSelfLocal定义为全局变量,那么此处strongSelf不会被release,所以仍然保持对self的强引用,则会导致循环引用。此处定义strong变量的用意是防止self被释放,导致此处的操作失效。
    };
    
    self.block(100);
    
    NSLog(@"%@", weakSelf);//此处self没有被释放,所以对它的弱引用变量weakSelf不为null。
    
    //关于注册通知中的block:The block is copied by the notification center and (the copy) held until the observer registration is removed.可见block中不能持有self的强引用,否则只有解除注册的时候,才能释放对self的持有,而self只有释放的时候才会在dealloc中解除通知的注册,则导致循环引用。所以要用弱引用代替self。
    [[NSNotificationCenter defaultCenter]addObserverForName:@"blockretaincycle" object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note) {
        [weakSelfLocal.btn setTitle:[NSString stringWithFormat:@"%d", 888] forState:UIControlStateNormal];
        [weakSelfLocal.btn setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
    }];
    
    //runloop对timer强引用,timer的target又强引用了self,self又强引用了timer,所以会循环引用,不会调用dealloc。
    self.timer = [NSTimer scheduledTimerWithTimeInterval:1.0
                                     target:self
                                   selector:@selector(timerMethod)
                                   userInfo:nil
                                    repeats:YES];
}

- (void)timerMethod{
    NSLog(@"%@", [NSDate date]);
}

- (void)tapAction:(UIButton *)sender{
    //很多系统自带的block里,可以直接用self,不会循环引用。不清楚的可以看苹果官方文档。
    [UIView animateWithDuration:1.0 animations:^{
        [self.view1 setFrame:CGRectMake(100, 400, 70, 70)];
    } completion:^(BOOL finished) {
        [self.view1 setFrame:CGRectMake(100, 400, 70, 70)];
    }];
}

- (void)dealloc{
    NSLog(@"%@", weakSelf);//weakSelf是全局的,而此处打印为(null),说明self已经被释放。如果导致循环引用,则不会调用这个方法。
    [[NSNotificationCenter defaultCenter]removeObserver:self name:@"blockretaincycle" object:nil];
//    [self.timer invalidate];//写在这里没用,不会调用的
}

- (void)viewWillDisappear:(BOOL)animated{
    [super viewWillDisappear:animated];
    [self.timer invalidate];//写在这里,确保退出页面的时候,timer失效,不会对self强引用,self可以销毁了。
}

@end

### 彻底释放变量并确保内存回收 在 Python 中,垃圾收集主要依赖于引用计数机制以及周期性的循环检测器来管理对象生命周期。当不再有活动的引用指向某个对象时,该对象占用的内存在大多数情况下会被自动回收。 然而,在某些特殊场景下可能希望显式地控制资源释放过程: #### 使用 `del` 关键字删除名称绑定 通过使用 `del` 可以解除一个名字到对象之间的关联[^1]: ```python a = "large string" b = a # b references the same object as a does now. del a # Only removes 'a' reference but not the actual data yet because 'b' still holds one. ``` #### 断开大对象间的相互引用 对于复杂数据结构如列表、类实例等,如果这些实体之间形成了环形引用,则即使外部已经没有任何地方持有它们的引用,内部互相持有的引用也会阻止其立即被销毁。此时可以考虑手动打破这种关系: ```python class Node: def __init__(self): self.next_node = None node_a = Node() node_b = Node() node_a.next_node = node_b node_b.next_node = node_a # Creates circular reference. # To break cycle before deletion: node_a.next_node = None node_b.next_node = None ``` #### 利用上下文管理协议清理资源 一些类型的对象实现了上下文管理接口 (`__enter__`, `__exit__`) ,可以在退出作用域时执行必要的清除操作。这通常用于文件处理或网络连接等情况下的资源管理。 ```python with open('example.txt', mode='w') as file_obj: pass # File will be closed automatically when exiting block. ``` 需要注意的是,Python 的解释器会在适当的时候触发垃圾收集程序运行,因此一般不需要开发者频繁干预此过程。只有在特定的应用环境中才需特别关注这一点,比如长时间运行的服务端应用或是涉及大量临时分配的小型对象创建与销毁的情况。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值